mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-20 06:00:44 +00:00
Merge remote-tracking branch 'origin/integration/sprint-28' into feature/CTLE-503-optimization-weather-api-data
# Conflicts: # qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/tables/QueryAction.java # qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/frontend/QFrontendTableMetaData.java # qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/actions/BaseAPIActionUtil.java # qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/actions/QRecordApiAdapter.java
This commit is contained in:
@ -42,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
@ -74,7 +75,7 @@ public class RunRecordScriptAutomationHandler extends RecordAutomationHandler
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(ScriptRevision.TABLE_NAME);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, scriptId)));
|
||||
queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin("currentScriptRevision")));
|
||||
queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin(ScriptsMetaDataProvider.CURRENT_SCRIPT_REVISION_JOIN_NAME)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOu
|
||||
** Interface for the Aggregate action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface AggregateInterface
|
||||
public interface AggregateInterface extends BaseQueryInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.interfaces;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for "query" (e.g., read-operations) action interfaces (query, count, aggregate).
|
||||
** Initially just here for the QueryStat methods - if we expand those to apply
|
||||
** to insert/update/delete, well, then rename this maybe to BaseActionInterface?
|
||||
*******************************************************************************/
|
||||
public interface BaseQueryInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default void setQueryStat(QueryStat queryStat)
|
||||
{
|
||||
//////////
|
||||
// noop //
|
||||
//////////
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default QueryStat getQueryStat()
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default void setQueryStatFirstResultTime()
|
||||
{
|
||||
QueryStat queryStat = getQueryStat();
|
||||
if(queryStat != null)
|
||||
{
|
||||
if(queryStat.getFirstResultTimestamp() == null)
|
||||
{
|
||||
queryStat.setFirstResultTimestamp(Instant.now());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
** Interface for the Count action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface CountInterface
|
||||
public interface CountInterface extends BaseQueryInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -31,10 +31,11 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
** Interface for the Query action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface QueryInterface
|
||||
public interface QueryInterface extends BaseQueryInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
QueryOutput execute(QueryInput queryInput) throws QException;
|
||||
|
||||
}
|
||||
|
@ -23,22 +23,33 @@ package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.ScriptExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.AbstractRunScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
@ -133,7 +144,17 @@ public class ExecuteCodeAction
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision)
|
||||
public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision) throws QException
|
||||
{
|
||||
return setupExecuteCodeInput(input, scriptRevision, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static ExecuteCodeInput setupExecuteCodeInput(AbstractRunScriptInput<?> input, ScriptRevision scriptRevision, String fileName) throws QException
|
||||
{
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput();
|
||||
executeCodeInput.setInput(new HashMap<>(Objects.requireNonNullElseGet(input.getInputValues(), HashMap::new)));
|
||||
@ -150,7 +171,49 @@ public class ExecuteCodeAction
|
||||
context.put("scriptUtils", input.getScriptUtils());
|
||||
}
|
||||
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
|
||||
if(CollectionUtils.nullSafeIsEmpty(scriptRevision.getFiles()))
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(ScriptRevisionFile.TABLE_NAME);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, scriptRevision.getId())));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
scriptRevision.setFiles(new ArrayList<>());
|
||||
for(QRecord record : queryOutput.getRecords())
|
||||
{
|
||||
scriptRevision.getFiles().add(new ScriptRevisionFile(record));
|
||||
}
|
||||
}
|
||||
|
||||
List<ScriptRevisionFile> files = scriptRevision.getFiles();
|
||||
if(files == null || files.isEmpty())
|
||||
{
|
||||
throw (new QException("Script Revision " + scriptRevision.getId() + " had more than 1 associated ScriptRevisionFile (and the name to use was not specified)."));
|
||||
}
|
||||
else
|
||||
{
|
||||
String contents = null;
|
||||
if(fileName == null || files.size() == 1)
|
||||
{
|
||||
contents = files.get(0).getContents();
|
||||
}
|
||||
else
|
||||
{
|
||||
for(ScriptRevisionFile file : files)
|
||||
{
|
||||
if(file.getFileName().equals(fileName))
|
||||
{
|
||||
contents = file.getContents();
|
||||
}
|
||||
}
|
||||
if(contents == null)
|
||||
{
|
||||
throw (new QException("Could not find file named " + fileName + " for Script Revision " + scriptRevision.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(contents).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
|
||||
}
|
||||
|
||||
ExecuteCodeAction.addApiUtilityToContext(context, scriptRevision);
|
||||
context.put("qqq", new QqqScriptUtils());
|
||||
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReference;
|
||||
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.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class RecordScriptTestInterface implements TestScriptActionInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void setupTestScriptInput(TestScriptInput testScriptInput, ExecuteCodeInput executeCodeInput) throws QException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void execute(TestScriptInput input, TestScriptOutput output) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
Serializable scriptId = input.getInputValues().get("scriptId");
|
||||
QRecord script = new GetAction().executeForRecord(new GetInput(Script.TABLE_NAME).withPrimaryKey(scriptId));
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// look up the records being tested against //
|
||||
//////////////////////////////////////////////
|
||||
String tableName = script.getValueString("tableName");
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
if(table == null)
|
||||
{
|
||||
throw (new QException("Could not find table [" + tableName + "] for script"));
|
||||
}
|
||||
|
||||
String recordPrimaryKeyList = ValueUtils.getValueAsString(input.getInputValues().get("recordPrimaryKeyList"));
|
||||
if(!StringUtils.hasContent(recordPrimaryKeyList))
|
||||
{
|
||||
throw (new QException("Record primary key list was not given."));
|
||||
}
|
||||
|
||||
QueryOutput queryOutput = new QueryAction().execute(new QueryInput(tableName)
|
||||
.withFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordPrimaryKeyList.split(","))))
|
||||
.withIncludeAssociations(true));
|
||||
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
|
||||
{
|
||||
throw (new QException("No records were found by the given primary keys."));
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// set up & run the action //
|
||||
/////////////////////////////
|
||||
RunAdHocRecordScriptInput runAdHocRecordScriptInput = new RunAdHocRecordScriptInput();
|
||||
runAdHocRecordScriptInput.setRecordList(queryOutput.getRecords());
|
||||
|
||||
BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null);
|
||||
runAdHocRecordScriptInput.setLogger(executionLogger);
|
||||
|
||||
runAdHocRecordScriptInput.setTableName(tableName);
|
||||
runAdHocRecordScriptInput.setCodeReference((AdHocScriptCodeReference) input.getCodeReference());
|
||||
RunAdHocRecordScriptOutput runAdHocRecordScriptOutput = new RunAdHocRecordScriptOutput();
|
||||
new RunAdHocRecordScriptAction().run(runAdHocRecordScriptInput, runAdHocRecordScriptOutput);
|
||||
|
||||
/////////////////////////////////
|
||||
// send outputs back to caller //
|
||||
/////////////////////////////////
|
||||
output.setScriptLog(executionLogger.getScriptLog());
|
||||
output.setScriptLogLines(executionLogger.getScriptLogLines());
|
||||
if(runAdHocRecordScriptOutput.getException().isPresent())
|
||||
{
|
||||
output.setException(runAdHocRecordScriptOutput.getException().get());
|
||||
}
|
||||
}
|
||||
catch(QException e)
|
||||
{
|
||||
output.setException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<QFieldMetaData> getTestInputFields()
|
||||
{
|
||||
return (List.of(new QFieldMetaData("recordPrimaryKeyList", QFieldType.STRING).withLabel("Record Primary Key List")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public List<QFieldMetaData> getTestOutputFields()
|
||||
{
|
||||
return (Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
@ -51,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReferen
|
||||
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.scripts.ScriptRevision;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
@ -197,7 +198,7 @@ public class RunAdHocRecordScriptAction
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(ScriptRevision.TABLE_NAME);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("script.id", QCriteriaOperator.EQUALS, codeReference.getScriptId())));
|
||||
queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin("currentScriptRevision")));
|
||||
queryInput.withQueryJoin(new QueryJoin(Script.TABLE_NAME).withBaseTableOrAlias(ScriptRevision.TABLE_NAME).withJoinMetaData(QContext.getQInstance().getJoin(ScriptsMetaDataProvider.CURRENT_SCRIPT_REVISION_JOIN_NAME)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
||||
|
@ -110,6 +110,7 @@ public class RunAssociatedScriptAction
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName("scriptRevision");
|
||||
getInput.setPrimaryKey(scriptRevisionId);
|
||||
getInput.setIncludeAssociations(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
|
@ -31,6 +31,8 @@ import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
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.actions.scripts.StoreAssociatedScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
@ -47,6 +49,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
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.processes.implementations.scripts.StoreScriptRevisionProcessStep;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
@ -180,41 +183,19 @@ public class StoreAssociatedScriptAction
|
||||
}
|
||||
}
|
||||
|
||||
QRecord scriptRevision = new QRecord()
|
||||
.withValue("scriptId", script.getValue("id"))
|
||||
.withValue("contents", input.getCode())
|
||||
.withValue("apiName", input.getApiName())
|
||||
.withValue("apiVersion", input.getApiVersion())
|
||||
.withValue("commitMessage", commitMessage)
|
||||
.withValue("sequenceNo", nextSequenceNo);
|
||||
|
||||
try
|
||||
{
|
||||
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
scriptRevision.setValue("author", "Unknown");
|
||||
}
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName("scriptRevision");
|
||||
insertInput.setRecords(List.of(scriptRevision));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
scriptRevision = insertOutput.getRecords().get(0);
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// update the script to point at the new revision //
|
||||
////////////////////////////////////////////////////
|
||||
script.setValue("currentScriptRevisionId", scriptRevision.getValue("id"));
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(script));
|
||||
new UpdateAction().execute(updateInput);
|
||||
RunBackendStepInput storeScriptRevisionInput = new RunBackendStepInput();
|
||||
storeScriptRevisionInput.addValue("scriptId", script.getValue("id"));
|
||||
storeScriptRevisionInput.addValue("commitMessage", commitMessage);
|
||||
storeScriptRevisionInput.addValue("apiName", input.getApiName());
|
||||
storeScriptRevisionInput.addValue("apiVersion", input.getApiVersion());
|
||||
storeScriptRevisionInput.addValue("fileNames", "script");
|
||||
storeScriptRevisionInput.addValue("fileContents:script", input.getCode());
|
||||
RunBackendStepOutput storeScriptRevisionOutput = new RunBackendStepOutput();
|
||||
new StoreScriptRevisionProcessStep().run(storeScriptRevisionInput, storeScriptRevisionOutput);
|
||||
|
||||
output.setScriptId(script.getValueInteger("id"));
|
||||
output.setScriptName(script.getValueString("name"));
|
||||
output.setScriptRevisionId(scriptRevision.getValueInteger("id"));
|
||||
output.setScriptRevisionSequenceNo(scriptRevision.getValueInteger("sequenceNo"));
|
||||
output.setScriptRevisionId(storeScriptRevisionOutput.getValueInteger("scriptRevisionId"));
|
||||
output.setScriptRevisionSequenceNo(storeScriptRevisionOutput.getValueInteger("scriptRevisionSequenceNo"));
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
/*******************************************************************************
|
||||
** Interface to provide logging functionality to QCodeExecution (e.g., scripts)
|
||||
*******************************************************************************/
|
||||
public interface QCodeExecutionLoggerInterface
|
||||
public interface QCodeExecutionLoggerInterface extends Serializable
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -23,9 +23,14 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
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.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;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
@ -43,11 +48,20 @@ public class AggregateAction
|
||||
{
|
||||
ActionHelper.validateSession(aggregateInput);
|
||||
|
||||
QTableMetaData table = aggregateInput.getTable();
|
||||
QBackendMetaData backend = aggregateInput.getBackend();
|
||||
|
||||
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, aggregateInput.getFilter());
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(aggregateInput.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
AggregateOutput aggregateOutput = qModule.getAggregateInterface().execute(aggregateInput);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
AggregateInterface aggregateInterface = qModule.getAggregateInterface();
|
||||
aggregateInterface.setQueryStat(queryStat);
|
||||
AggregateOutput aggregateOutput = aggregateInterface.execute(aggregateInput);
|
||||
|
||||
QueryStatManager.getInstance().add(queryStat);
|
||||
|
||||
return aggregateOutput;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,14 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
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.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;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
@ -43,11 +48,20 @@ public class CountAction
|
||||
{
|
||||
ActionHelper.validateSession(countInput);
|
||||
|
||||
QTableMetaData table = countInput.getTable();
|
||||
QBackendMetaData backend = countInput.getBackend();
|
||||
|
||||
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, countInput.getFilter());
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
CountOutput countOutput = qModule.getCountInterface().execute(countInput);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
||||
|
||||
CountInterface countInterface = qModule.getCountInterface();
|
||||
countInterface.setQueryStat(queryStat);
|
||||
CountOutput countOutput = countInterface.execute(countInput);
|
||||
|
||||
QueryStatManager.getInstance().add(queryStat);
|
||||
|
||||
return countOutput;
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,16 @@ public class GetAction
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QRecord executeForRecord(GetInput getInput) throws QException
|
||||
{
|
||||
return (execute(getInput).getRecord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -78,6 +78,28 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QRecord executeForRecord(InsertInput insertInput) throws QException
|
||||
{
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
return (insertOutput.getRecords().get(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> executeForRecords(InsertInput insertInput) throws QException
|
||||
{
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
return (insertOutput.getRecords());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostQueryCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.BufferedRecordPipe;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipeBufferedWrapper;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryActionCacheHelper;
|
||||
@ -48,12 +49,14 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
@ -94,6 +97,7 @@ public class QueryAction
|
||||
throw (new QException("A table named [" + queryInput.getTableName() + "] was not found in the active QInstance"));
|
||||
}
|
||||
|
||||
QBackendMetaData backend = queryInput.getBackend();
|
||||
postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(AbstractPostQueryCustomizer.class, table, TableCustomizers.POST_QUERY_RECORD.getRole());
|
||||
this.queryInput = queryInput;
|
||||
|
||||
@ -111,11 +115,16 @@ public class QueryAction
|
||||
}
|
||||
}
|
||||
|
||||
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, queryInput.getFilter());
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(queryInput.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
QueryOutput queryOutput = qModule.getQueryInterface().execute(queryInput);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(backend);
|
||||
|
||||
QueryInterface queryInterface = qModule.getQueryInterface();
|
||||
queryInterface.setQueryStat(queryStat);
|
||||
QueryOutput queryOutput = queryInterface.execute(queryInput);
|
||||
|
||||
QueryStatManager.getInstance().add(queryStat);
|
||||
|
||||
////////////////////////////
|
||||
// handle cache use-cases //
|
||||
|
@ -83,6 +83,28 @@ public class UpdateAction
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QRecord executeForRecord(UpdateInput updateInput) throws QException
|
||||
{
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
return (updateOutput.getRecords().get(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> executeForRecords(UpdateInput updateInput) throws QException
|
||||
{
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(updateInput);
|
||||
return (updateOutput.getRecords());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,640 @@
|
||||
/*
|
||||
* 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.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
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.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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Singleton, which starts a thread, to store query stats into a table.
|
||||
**
|
||||
** Supports these systemProperties or ENV_VARS:
|
||||
** qqq.queryStatManager.enabled / QQQ_QUERY_STAT_MANAGER_ENABLED
|
||||
** qqq.queryStatManager.minMillisToStore / QQQ_QUERY_STAT_MANAGER_MIN_MILLIS_TO_STORE
|
||||
** qqq.queryStatManager.jobPeriodSeconds / QQQ_QUERY_STAT_MANAGER_JOB_PERIOD_SECONDS
|
||||
** qqq.queryStatManager.jobInitialDelay / QQQ_QUERY_STAT_MANAGER_JOB_INITIAL_DELAY
|
||||
*******************************************************************************/
|
||||
public class QueryStatManager
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QueryStatManager.class);
|
||||
|
||||
private static QueryStatManager queryStatManager = null;
|
||||
|
||||
// todo - support multiple qInstances?
|
||||
private QInstance qInstance;
|
||||
private Supplier<QSession> sessionSupplier;
|
||||
|
||||
private boolean active = false;
|
||||
private List<QueryStat> queryStats = new ArrayList<>();
|
||||
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
private int jobPeriodSeconds = 60;
|
||||
private int jobInitialDelay = 60;
|
||||
private int minMillisToStore = 0;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Singleton constructor
|
||||
*******************************************************************************/
|
||||
private QueryStatManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Singleton accessor
|
||||
*******************************************************************************/
|
||||
public static QueryStatManager getInstance()
|
||||
{
|
||||
if(queryStatManager == null)
|
||||
{
|
||||
queryStatManager = new QueryStatManager();
|
||||
|
||||
QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();
|
||||
|
||||
Integer propertyMinMillisToStore = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.minMillisToStore", "QQQ_QUERY_STAT_MANAGER_MIN_MILLIS_TO_STORE", null);
|
||||
if(propertyMinMillisToStore != null)
|
||||
{
|
||||
queryStatManager.setMinMillisToStore(propertyMinMillisToStore);
|
||||
}
|
||||
|
||||
Integer propertyJobPeriodSeconds = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.jobPeriodSeconds", "QQQ_QUERY_STAT_MANAGER_JOB_PERIOD_SECONDS", null);
|
||||
if(propertyJobPeriodSeconds != null)
|
||||
{
|
||||
queryStatManager.setJobPeriodSeconds(propertyJobPeriodSeconds);
|
||||
}
|
||||
|
||||
Integer propertyJobInitialDelay = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.jobInitialDelay", "QQQ_QUERY_STAT_MANAGER_JOB_INITIAL_DELAY", null);
|
||||
if(propertyJobInitialDelay != null)
|
||||
{
|
||||
queryStatManager.setJobInitialDelay(propertyJobInitialDelay);
|
||||
}
|
||||
|
||||
}
|
||||
return (queryStatManager);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QueryStat newQueryStat(QBackendMetaData backend, QTableMetaData table, QQueryFilter filter)
|
||||
{
|
||||
QueryStat queryStat = null;
|
||||
|
||||
if(table.isCapabilityEnabled(backend, Capability.QUERY_STATS))
|
||||
{
|
||||
queryStat = new QueryStat();
|
||||
queryStat.setTableName(table.getName());
|
||||
queryStat.setQueryFilter(Objects.requireNonNullElse(filter, new QQueryFilter()));
|
||||
queryStat.setStartTimestamp(Instant.now());
|
||||
}
|
||||
|
||||
return (queryStat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void start(QInstance qInstance, Supplier<QSession> sessionSupplier)
|
||||
{
|
||||
if(!isEnabled())
|
||||
{
|
||||
LOG.info("Not starting QueryStatManager per settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Starting QueryStatManager");
|
||||
|
||||
this.qInstance = qInstance;
|
||||
this.sessionSupplier = sessionSupplier;
|
||||
|
||||
active = true;
|
||||
queryStats = new ArrayList<>();
|
||||
|
||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
executorService.scheduleAtFixedRate(new QueryStatManagerInsertJob(), jobInitialDelay, jobPeriodSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean isEnabled()
|
||||
{
|
||||
return new QMetaDataVariableInterpreter().getBooleanFromPropertyOrEnvironment("qqq.queryStatManager.enabled", "QQQ_QUERY_STAT_MANAGER_ENABLED", true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void stop()
|
||||
{
|
||||
active = false;
|
||||
queryStats.clear();
|
||||
|
||||
if(executorService != null)
|
||||
{
|
||||
executorService.shutdown();
|
||||
executorService = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void add(QueryStat queryStat)
|
||||
{
|
||||
if(queryStat == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(active)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set fields that we need to capture now (rather than when the thread to store runs) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(queryStat.getFirstResultTimestamp() == null)
|
||||
{
|
||||
queryStat.setFirstResultTimestamp(Instant.now());
|
||||
}
|
||||
|
||||
if(queryStat.getStartTimestamp() != null && queryStat.getFirstResultTimestamp() != null && queryStat.getFirstResultMillis() == null)
|
||||
{
|
||||
long millis = queryStat.getFirstResultTimestamp().toEpochMilli() - queryStat.getStartTimestamp().toEpochMilli();
|
||||
queryStat.setFirstResultMillis((int) millis);
|
||||
}
|
||||
|
||||
if(queryStat.getFirstResultMillis() != null && queryStat.getFirstResultMillis() < minMillisToStore)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////
|
||||
// discard this record if it's under the min millis setting //
|
||||
//////////////////////////////////////////////////////////////
|
||||
return;
|
||||
}
|
||||
|
||||
if(queryStat.getSessionId() == null && QContext.getQSession() != null)
|
||||
{
|
||||
queryStat.setSessionId(QContext.getQSession().getUuid());
|
||||
}
|
||||
|
||||
if(queryStat.getAction() == null)
|
||||
{
|
||||
if(!QContext.getActionStack().isEmpty())
|
||||
{
|
||||
queryStat.setAction(QContext.getActionStack().peek().getActionIdentity());
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean expected = false;
|
||||
Exception e = new Exception("Unexpected empty action stack");
|
||||
for(StackTraceElement stackTraceElement : e.getStackTrace())
|
||||
{
|
||||
String className = stackTraceElement.getClassName();
|
||||
if(className.contains(QueryStatManagerInsertJob.class.getName()))
|
||||
{
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!expected)
|
||||
{
|
||||
LOG.debug(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
queryStats.add(queryStat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private List<QueryStat> getListAndReset()
|
||||
{
|
||||
if(queryStats.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
synchronized(this)
|
||||
{
|
||||
List<QueryStat> returnList = queryStats;
|
||||
queryStats = new ArrayList<>();
|
||||
return (returnList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** force stats to be stored right now (rather than letting the scheduled job do it)
|
||||
*******************************************************************************/
|
||||
public void storeStatsNow()
|
||||
{
|
||||
new QueryStatManagerInsertJob().run();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runnable that gets scheduled to periodically reset and store the list of collected stats
|
||||
*******************************************************************************/
|
||||
private static class QueryStatManagerInsertJob implements Runnable
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QueryStatManagerInsertJob.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
QContext.init(getInstance().qInstance, getInstance().sessionSupplier.get());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// every time we re-run, check if we've been turned off - if so, stop the service. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
if(!isEnabled())
|
||||
{
|
||||
LOG.info("Stopping QueryStatManager.");
|
||||
getInstance().stop();
|
||||
return;
|
||||
}
|
||||
|
||||
List<QueryStat> list = getInstance().getListAndReset();
|
||||
|
||||
LOG.info(logPair("queryStatListSize", list.size()));
|
||||
|
||||
if(list.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// prime the entities for storing //
|
||||
////////////////////////////////////
|
||||
List<QRecord> queryStatQRecordsToInsert = new ArrayList<>();
|
||||
for(QueryStat queryStat : list)
|
||||
{
|
||||
try
|
||||
{
|
||||
//////////////////////
|
||||
// set the table id //
|
||||
//////////////////////
|
||||
Integer qqqTableId = getQQQTableId(queryStat.getTableName());
|
||||
queryStat.setQqqTableId(qqqTableId);
|
||||
|
||||
//////////////////////////////
|
||||
// build join-table records //
|
||||
//////////////////////////////
|
||||
if(CollectionUtils.nullSafeHasContents(queryStat.getJoinTableNames()))
|
||||
{
|
||||
List<QueryStatJoinTable> queryStatJoinTableList = new ArrayList<>();
|
||||
for(String joinTableName : queryStat.getJoinTableNames())
|
||||
{
|
||||
queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(getQQQTableId(joinTableName)));
|
||||
}
|
||||
queryStat.setQueryStatJoinTableList(queryStatJoinTableList);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// build criteria records //
|
||||
////////////////////////////
|
||||
if(queryStat.getQueryFilter() != null && queryStat.getQueryFilter().hasAnyCriteria())
|
||||
{
|
||||
List<QueryStatCriteriaField> queryStatCriteriaFieldList = new ArrayList<>();
|
||||
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryStat.getQueryFilter());
|
||||
queryStat.setQueryStatCriteriaFieldList(queryStatCriteriaFieldList);
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(queryStat.getQueryFilter().getOrderBys()))
|
||||
{
|
||||
List<QueryStatOrderByField> queryStatOrderByFieldList = new ArrayList<>();
|
||||
processOrderByFromFilter(qqqTableId, queryStatOrderByFieldList, queryStat.getQueryFilter());
|
||||
queryStat.setQueryStatOrderByFieldList(queryStatOrderByFieldList);
|
||||
}
|
||||
|
||||
queryStatQRecordsToInsert.add(queryStat.toQRecord());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//////////////////////
|
||||
// skip this record //
|
||||
//////////////////////
|
||||
LOG.warn("Error priming a query stat for storing", e);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QueryStat.TABLE_NAME);
|
||||
insertInput.setRecords(queryStatQRecordsToInsert);
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error inserting query stats", e);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error storing query stats", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
QContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processCriteriaFromFilter(Integer qqqTableId, List<QueryStatCriteriaField> queryStatCriteriaFieldList, QQueryFilter queryFilter) throws QException
|
||||
{
|
||||
for(QFilterCriteria criteria : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||
{
|
||||
String fieldName = criteria.getFieldName();
|
||||
QueryStatCriteriaField queryStatCriteriaField = new QueryStatCriteriaField();
|
||||
queryStatCriteriaField.setOperator(String.valueOf(criteria.getOperator()));
|
||||
|
||||
if(criteria.getValues() != null)
|
||||
{
|
||||
queryStatCriteriaField.setValues(StringUtils.join(",", criteria.getValues()));
|
||||
}
|
||||
|
||||
if(fieldName.contains("."))
|
||||
{
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatCriteriaField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatCriteriaField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryStatCriteriaField.setQqqTableId(qqqTableId);
|
||||
queryStatCriteriaField.setName(fieldName);
|
||||
}
|
||||
|
||||
queryStatCriteriaFieldList.add(queryStatCriteriaField);
|
||||
}
|
||||
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
|
||||
{
|
||||
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, subFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processOrderByFromFilter(Integer qqqTableId, List<QueryStatOrderByField> queryStatOrderByFieldList, QQueryFilter queryFilter) throws QException
|
||||
{
|
||||
for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys()))
|
||||
{
|
||||
String fieldName = orderBy.getFieldName();
|
||||
QueryStatOrderByField queryStatOrderByField = new QueryStatOrderByField();
|
||||
|
||||
if(fieldName != null)
|
||||
{
|
||||
if(fieldName.contains("."))
|
||||
{
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatOrderByField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatOrderByField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryStatOrderByField.setQqqTableId(qqqTableId);
|
||||
queryStatOrderByField.setName(fieldName);
|
||||
}
|
||||
|
||||
queryStatOrderByFieldList.add(queryStatOrderByField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for jobPeriodSeconds
|
||||
*******************************************************************************/
|
||||
public int getJobPeriodSeconds()
|
||||
{
|
||||
return (this.jobPeriodSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for jobPeriodSeconds
|
||||
*******************************************************************************/
|
||||
public void setJobPeriodSeconds(int jobPeriodSeconds)
|
||||
{
|
||||
this.jobPeriodSeconds = jobPeriodSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for jobPeriodSeconds
|
||||
*******************************************************************************/
|
||||
public QueryStatManager withJobPeriodSeconds(int jobPeriodSeconds)
|
||||
{
|
||||
this.jobPeriodSeconds = jobPeriodSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for jobInitialDelay
|
||||
*******************************************************************************/
|
||||
public int getJobInitialDelay()
|
||||
{
|
||||
return (this.jobInitialDelay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for jobInitialDelay
|
||||
*******************************************************************************/
|
||||
public void setJobInitialDelay(int jobInitialDelay)
|
||||
{
|
||||
this.jobInitialDelay = jobInitialDelay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for jobInitialDelay
|
||||
*******************************************************************************/
|
||||
public QueryStatManager withJobInitialDelay(int jobInitialDelay)
|
||||
{
|
||||
this.jobInitialDelay = jobInitialDelay;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for minMillisToStore
|
||||
*******************************************************************************/
|
||||
public int getMinMillisToStore()
|
||||
{
|
||||
return (this.minMillisToStore);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for minMillisToStore
|
||||
*******************************************************************************/
|
||||
public void setMinMillisToStore(int minMillisToStore)
|
||||
{
|
||||
this.minMillisToStore = minMillisToStore;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for minMillisToStore
|
||||
*******************************************************************************/
|
||||
public QueryStatManager withMinMillisToStore(int minMillisToStore)
|
||||
{
|
||||
this.minMillisToStore = minMillisToStore;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -26,8 +26,8 @@ import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfInput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.ConvertHtmlToPdfOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.ConvertHtmlToPdfOutput;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
|
@ -28,8 +28,8 @@ import com.kingsrook.qqq.backend.core.actions.AbstractQActionFunction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.TemplateType;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
@ -62,7 +62,15 @@ public class RenderTemplateAction extends AbstractQActionFunction<RenderTemplate
|
||||
if(TemplateType.VELOCITY.equals(input.getTemplateType()))
|
||||
{
|
||||
Velocity.init();
|
||||
Context context = new VelocityContext(input.getContext());
|
||||
Context context = new VelocityContext();
|
||||
|
||||
if(input.getContext() != null)
|
||||
{
|
||||
for(Map.Entry<String, ?> entry : input.getContext().entrySet())
|
||||
{
|
||||
context.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
setupEventHandlers(context);
|
||||
|
||||
|
@ -1224,11 +1224,10 @@ public class QInstanceValidator
|
||||
QScheduleMetaData schedule = process.getSchedule();
|
||||
assertCondition(schedule.getRepeatMillis() != null || schedule.getRepeatSeconds() != null, "Either repeat millis or repeat seconds must be set on schedule in process " + processName);
|
||||
|
||||
if(schedule.getBackendVariant() != null)
|
||||
if(schedule.getVariantBackend() != null)
|
||||
{
|
||||
assertCondition(schedule.getVariantRunStrategy() != null, "A variant strategy was not set for " + schedule.getBackendVariant() + " on schedule in process " + processName);
|
||||
assertCondition(schedule.getVariantTableName() != null, "A variant table name was not set for " + schedule.getBackendVariant() + " on schedule in process " + processName);
|
||||
assertCondition(schedule.getVariantFieldName() != null, "A variant field name was not set for " + schedule.getBackendVariant() + " on schedule in process " + processName);
|
||||
assertCondition(qInstance.getBackend(schedule.getVariantBackend()) != null, "A variant backend was not found for " + schedule.getVariantBackend());
|
||||
assertCondition(schedule.getVariantRunStrategy() != null, "A variant run strategy was not set for " + schedule.getVariantBackend() + " on schedule in process " + processName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import io.github.cdimascio.dotenv.Dotenv;
|
||||
import io.github.cdimascio.dotenv.DotenvEntry;
|
||||
@ -266,4 +267,111 @@ public class QMetaDataVariableInterpreter
|
||||
|
||||
valueMaps.put(name, values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** First look for a boolean ("true" or "false") in the specified system property -
|
||||
** Next look for a boolean in the specified env var name -
|
||||
** Finally return the default.
|
||||
*******************************************************************************/
|
||||
public boolean getBooleanFromPropertyOrEnvironment(String systemPropertyName, String environmentVariableName, boolean defaultIfNotSet)
|
||||
{
|
||||
String propertyValue = System.getProperty(systemPropertyName);
|
||||
if(StringUtils.hasContent(propertyValue))
|
||||
{
|
||||
if("false".equalsIgnoreCase(propertyValue))
|
||||
{
|
||||
LOG.info("Read system property [" + systemPropertyName + "] as boolean false.");
|
||||
return (false);
|
||||
}
|
||||
else if("true".equalsIgnoreCase(propertyValue))
|
||||
{
|
||||
LOG.info("Read system property [" + systemPropertyName + "] as boolean true.");
|
||||
return (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unrecognized boolean value [" + propertyValue + "] for system property [" + systemPropertyName + "].");
|
||||
}
|
||||
}
|
||||
|
||||
String envValue = interpret("${env." + environmentVariableName + "}");
|
||||
if(StringUtils.hasContent(envValue))
|
||||
{
|
||||
if("false".equalsIgnoreCase(envValue))
|
||||
{
|
||||
LOG.info("Read env var [" + environmentVariableName + "] as boolean false.");
|
||||
return (false);
|
||||
}
|
||||
else if("true".equalsIgnoreCase(envValue))
|
||||
{
|
||||
LOG.info("Read env var [" + environmentVariableName + "] as boolean true.");
|
||||
return (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unrecognized boolean value [" + envValue + "] for env var [" + environmentVariableName + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return defaultIfNotSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** First look for an Integer in the specified system property -
|
||||
** Next look for an Integer in the specified env var name -
|
||||
** Finally return the default (null allowed as default!)
|
||||
*******************************************************************************/
|
||||
public Integer getIntegerFromPropertyOrEnvironment(String systemPropertyName, String environmentVariableName, Integer defaultIfNotSet)
|
||||
{
|
||||
String propertyValue = System.getProperty(systemPropertyName);
|
||||
if(StringUtils.hasContent(propertyValue))
|
||||
{
|
||||
if(canParseAsInteger(propertyValue))
|
||||
{
|
||||
LOG.info("Read system property [" + systemPropertyName + "] as integer " + propertyValue);
|
||||
return (Integer.parseInt(propertyValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unrecognized integer value [" + propertyValue + "] for system property [" + systemPropertyName + "].");
|
||||
}
|
||||
}
|
||||
|
||||
String envValue = interpret("${env." + environmentVariableName + "}");
|
||||
if(StringUtils.hasContent(envValue))
|
||||
{
|
||||
if(canParseAsInteger(envValue))
|
||||
{
|
||||
LOG.info("Read env var [" + environmentVariableName + "] as integer " + environmentVariableName);
|
||||
return (Integer.parseInt(propertyValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unrecognized integer value [" + envValue + "] for env var [" + environmentVariableName + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return defaultIfNotSet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** we'd use NumberUtils.isDigits, but that doesn't allow negatives, or
|
||||
** numberUtils.isParseable, but that allows decimals, so...
|
||||
*******************************************************************************/
|
||||
private boolean canParseAsInteger(String value)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (value.matches("^-?[0-9]+$"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,6 +56,16 @@ public class AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getActionIdentity()
|
||||
{
|
||||
return (getClass().getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** performance instance validation (if not previously done).
|
||||
* // todo - verify this is happening (e.g., when context is set i guess)
|
||||
@ -145,14 +155,4 @@ public class AbstractActionInput
|
||||
this.asyncJobCallback = asyncJobCallback;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for instance
|
||||
*******************************************************************************/
|
||||
public AbstractActionInput withInstance(QInstance instance)
|
||||
{
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,17 @@ public class AbstractTableActionInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getActionIdentity()
|
||||
{
|
||||
return (getClass().getSimpleName() + ":" + getTableName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -76,6 +76,17 @@ public class RunProcessInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getActionIdentity()
|
||||
{
|
||||
return (getClass().getSimpleName() + ":" + getProcessName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** e.g., for steps after the first step in a process, seed the data in a run
|
||||
** function request from a process state.
|
||||
|
@ -54,6 +54,29 @@ public class DeleteInput extends AbstractTableActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public DeleteInput(String tableName)
|
||||
{
|
||||
setTableName(tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DeleteInput withTableName(String tableName)
|
||||
{
|
||||
super.withTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for transaction
|
||||
**
|
||||
|
@ -67,6 +67,29 @@ public class GetInput extends AbstractTableActionInput implements QueryOrGetInpu
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput(String tableName)
|
||||
{
|
||||
setTableName(tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public AbstractTableActionInput withTableName(String tableName)
|
||||
{
|
||||
super.withTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for primaryKey
|
||||
**
|
||||
|
@ -22,12 +22,15 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.insert;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -55,6 +58,71 @@ public class InsertInput extends AbstractTableActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public InsertInput(String tableName)
|
||||
{
|
||||
setTableName(tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public InsertInput withTableName(String tableName)
|
||||
{
|
||||
super.withTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public InsertInput withRecord(QRecord record)
|
||||
{
|
||||
if(records == null)
|
||||
{
|
||||
records = new ArrayList<>();
|
||||
}
|
||||
|
||||
records.add(record);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public InsertInput withRecordEntity(QRecordEntity recordEntity)
|
||||
{
|
||||
return (withRecord(recordEntity.toQRecord()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public InsertInput withRecordEntities(List<QRecordEntity> recordEntityList)
|
||||
{
|
||||
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
||||
{
|
||||
withRecordEntity(recordEntity);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for transaction
|
||||
**
|
||||
|
@ -78,6 +78,17 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QueryInput(String tableName)
|
||||
{
|
||||
setTableName(tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for filter
|
||||
**
|
||||
|
@ -22,12 +22,15 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.update;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -62,6 +65,71 @@ public class UpdateInput extends AbstractTableActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UpdateInput(String tableName)
|
||||
{
|
||||
setTableName(tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public UpdateInput withTableName(String tableName)
|
||||
{
|
||||
super.withTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UpdateInput withRecord(QRecord record)
|
||||
{
|
||||
if(records == null)
|
||||
{
|
||||
records = new ArrayList<>();
|
||||
}
|
||||
|
||||
records.add(record);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UpdateInput withRecordEntity(QRecordEntity recordEntity)
|
||||
{
|
||||
return (withRecord(recordEntity.toQRecord()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UpdateInput withRecordEntities(List<QRecordEntity> recordEntityList)
|
||||
{
|
||||
for(QRecordEntity recordEntity : CollectionUtils.nonNullList(recordEntityList))
|
||||
{
|
||||
withRecordEntity(recordEntity);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for transaction
|
||||
**
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 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/
|
||||
@ -19,7 +19,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.templates;
|
||||
package com.kingsrook.qqq.backend.core.model.actions.templates;
|
||||
|
||||
|
||||
import java.io.OutputStream;
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 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/
|
||||
@ -19,7 +19,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.templates;
|
||||
package com.kingsrook.qqq.backend.core.model.actions.templates;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 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/
|
||||
@ -19,11 +19,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.templates;
|
||||
package com.kingsrook.qqq.backend.core.model.actions.templates;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.TemplateType;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -35,7 +36,7 @@ public class RenderTemplateInput extends AbstractActionInput
|
||||
private String code; // todo - TemplateReference, like CodeReference??
|
||||
private TemplateType templateType;
|
||||
|
||||
private Map<String, Object> context;
|
||||
private Map<String, ? extends Object> context;
|
||||
|
||||
|
||||
|
||||
@ -120,7 +121,7 @@ public class RenderTemplateInput extends AbstractActionInput
|
||||
** Getter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Object> getContext()
|
||||
public Map<String, ? extends Object> getContext()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
@ -131,7 +132,7 @@ public class RenderTemplateInput extends AbstractActionInput
|
||||
** Setter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setContext(Map<String, Object> context)
|
||||
public void setContext(Map<String, ? extends Object> context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 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/
|
||||
@ -19,7 +19,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.templates;
|
||||
package com.kingsrook.qqq.backend.core.model.actions.templates;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
@ -50,6 +50,17 @@ public class RenderWidgetInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getActionIdentity()
|
||||
{
|
||||
return (getClass().getSimpleName() + ":" + widgetMetaData.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for widgetMetaData
|
||||
**
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.data;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Annotation to place onto fields in a QRecordEntity, to mark them as associated
|
||||
** record lists
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QAssociation
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
String name();
|
||||
|
||||
}
|
@ -23,8 +23,12 @@ package com.kingsrook.qqq.backend.core.model.data;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedParameterizedType;
|
||||
import java.lang.reflect.AnnotatedType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
@ -40,7 +44,9 @@ import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -50,7 +56,8 @@ public abstract class QRecordEntity
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QRecordEntity.class);
|
||||
|
||||
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityField> fieldMapping = new ListingHash<>();
|
||||
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityField> fieldMapping = new ListingHash<>();
|
||||
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityAssociation> associationMapping = new ListingHash<>();
|
||||
|
||||
private Map<String, Serializable> originalRecordValues;
|
||||
|
||||
@ -61,11 +68,23 @@ public abstract class QRecordEntity
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static <T extends QRecordEntity> T fromQRecord(Class<T> c, QRecord qRecord) throws QException
|
||||
{
|
||||
return (QRecordEntity.fromQRecord(c, qRecord, ""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Build an entity of this QRecord type from a QRecord - where the fields for
|
||||
** this entity have the given prefix - e.g., if they were selected as part of a join.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static <T extends QRecordEntity> T fromQRecord(Class<T> c, QRecord qRecord, String fieldNamePrefix) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
T entity = c.getConstructor().newInstance();
|
||||
entity.populateFromQRecord(qRecord);
|
||||
entity.populateFromQRecord(qRecord, fieldNamePrefix);
|
||||
return (entity);
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -80,19 +99,73 @@ public abstract class QRecordEntity
|
||||
** Build an entity of this QRecord type from a QRecord
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected <T extends QRecordEntity> void populateFromQRecord(QRecord qRecord) throws QRuntimeException
|
||||
protected void populateFromQRecord(QRecord qRecord) throws QRuntimeException
|
||||
{
|
||||
populateFromQRecord(qRecord, "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Build an entity of this QRecord type from a QRecord - where the fields for
|
||||
** this entity have the given prefix - e.g., if they were selected as part of a join.
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected <T extends QRecordEntity> void populateFromQRecord(QRecord qRecord, String fieldNamePrefix) throws QRuntimeException
|
||||
{
|
||||
try
|
||||
{
|
||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
||||
originalRecordValues = new HashMap<>();
|
||||
|
||||
if(fieldNamePrefix == null)
|
||||
{
|
||||
fieldNamePrefix = "";
|
||||
}
|
||||
|
||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
||||
{
|
||||
Serializable value = qRecord.getValue(qRecordEntityField.getFieldName());
|
||||
Serializable value = qRecord.getValue(fieldNamePrefix + qRecordEntityField.getFieldName());
|
||||
Object typedValue = qRecordEntityField.convertValueType(value);
|
||||
qRecordEntityField.getSetter().invoke(this, typedValue);
|
||||
originalRecordValues.put(qRecordEntityField.getFieldName(), value);
|
||||
}
|
||||
|
||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||
{
|
||||
List<QRecord> associatedRecords = qRecord.getAssociatedRecords().get(qRecordEntityAssociation.getAssociationAnnotation().name());
|
||||
if(associatedRecords == null)
|
||||
{
|
||||
qRecordEntityAssociation.getSetter().invoke(this, (Object) null);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<QRecordEntity> associatedEntityList = new ArrayList<>();
|
||||
for(QRecord associatedRecord : CollectionUtils.nonNullList(associatedRecords))
|
||||
{
|
||||
associatedEntityList.add(QRecordEntity.fromQRecord(qRecordEntityAssociation.getAssociatedType(), associatedRecord));
|
||||
}
|
||||
qRecordEntityAssociation.getSetter().invoke(this, associatedEntityList);
|
||||
}
|
||||
}
|
||||
|
||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||
{
|
||||
List<QRecord> associatedRecords = qRecord.getAssociatedRecords().get(qRecordEntityAssociation.getAssociationAnnotation().name());
|
||||
if(associatedRecords == null)
|
||||
{
|
||||
qRecordEntityAssociation.getSetter().invoke(this, (Object) null);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<QRecordEntity> associatedEntityList = new ArrayList<>();
|
||||
for(QRecord associatedRecord : CollectionUtils.nonNullList(associatedRecords))
|
||||
{
|
||||
associatedEntityList.add(QRecordEntity.fromQRecord(qRecordEntityAssociation.getAssociatedType(), associatedRecord));
|
||||
}
|
||||
qRecordEntityAssociation.getSetter().invoke(this, associatedEntityList);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -112,12 +185,30 @@ public abstract class QRecordEntity
|
||||
{
|
||||
QRecord qRecord = new QRecord();
|
||||
|
||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
||||
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
||||
{
|
||||
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
||||
}
|
||||
|
||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||
{
|
||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||
|
||||
if(associatedEntities != null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
||||
}
|
||||
|
||||
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
||||
{
|
||||
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
||||
}
|
||||
}
|
||||
|
||||
return (qRecord);
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -127,7 +218,6 @@ public abstract class QRecordEntity
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -137,8 +227,7 @@ public abstract class QRecordEntity
|
||||
{
|
||||
QRecord qRecord = new QRecord();
|
||||
|
||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
||||
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
||||
{
|
||||
Serializable thisValue = (Serializable) qRecordEntityField.getGetter().invoke(this);
|
||||
Serializable originalValue = null;
|
||||
@ -153,6 +242,25 @@ public abstract class QRecordEntity
|
||||
}
|
||||
}
|
||||
|
||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||
{
|
||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||
|
||||
if(associatedEntities != null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
||||
}
|
||||
|
||||
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
||||
{
|
||||
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
||||
}
|
||||
}
|
||||
|
||||
return (qRecord);
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -181,7 +289,15 @@ public abstract class QRecordEntity
|
||||
{
|
||||
String fieldName = getFieldNameFromGetter(possibleGetter);
|
||||
Optional<QField> fieldAnnotation = getQFieldAnnotation(c, fieldName);
|
||||
fieldList.add(new QRecordEntityField(fieldName, possibleGetter, setter.get(), possibleGetter.getReturnType(), fieldAnnotation.orElse(null)));
|
||||
|
||||
if(fieldAnnotation.isPresent())
|
||||
{
|
||||
fieldList.add(new QRecordEntityField(fieldName, possibleGetter, setter.get(), possibleGetter.getReturnType(), fieldAnnotation.orElse(null)));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Skipping field without @QField annotation", logPair("class", c.getSimpleName()), logPair("fieldName", fieldName));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -196,15 +312,73 @@ public abstract class QRecordEntity
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static List<QRecordEntityAssociation> getAssociationList(Class<? extends QRecordEntity> c)
|
||||
{
|
||||
if(!associationMapping.containsKey(c))
|
||||
{
|
||||
List<QRecordEntityAssociation> associationList = new ArrayList<>();
|
||||
for(Method possibleGetter : c.getMethods())
|
||||
{
|
||||
if(isGetter(possibleGetter))
|
||||
{
|
||||
Optional<Method> setter = getSetterForGetter(c, possibleGetter);
|
||||
|
||||
if(setter.isPresent())
|
||||
{
|
||||
String fieldName = getFieldNameFromGetter(possibleGetter);
|
||||
Optional<QAssociation> associationAnnotation = getQAssociationAnnotation(c, fieldName);
|
||||
|
||||
if(associationAnnotation.isPresent())
|
||||
{
|
||||
Class<? extends QRecordEntity> listTypeParam = (Class<? extends QRecordEntity>) getListTypeParam(possibleGetter.getReturnType(), possibleGetter.getAnnotatedReturnType());
|
||||
associationList.add(new QRecordEntityAssociation(fieldName, possibleGetter, setter.get(), listTypeParam, associationAnnotation.orElse(null)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Getter method [" + possibleGetter.getName() + "] does not have a corresponding setter.");
|
||||
}
|
||||
}
|
||||
}
|
||||
associationMapping.put(c, associationList);
|
||||
}
|
||||
return (associationMapping.get(c));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Optional<QField> getQFieldAnnotation(Class<? extends QRecordEntity> c, String fieldName)
|
||||
{
|
||||
return (getAnnotationOnField(c, QField.class, fieldName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Optional<QAssociation> getQAssociationAnnotation(Class<? extends QRecordEntity> c, String fieldName)
|
||||
{
|
||||
return (getAnnotationOnField(c, QAssociation.class, fieldName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static <A extends Annotation> Optional<A> getAnnotationOnField(Class<? extends QRecordEntity> c, Class<A> annotationClass, String fieldName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = c.getDeclaredField(fieldName);
|
||||
return (Optional.ofNullable(field.getAnnotation(QField.class)));
|
||||
return (Optional.ofNullable(field.getAnnotation(annotationClass)));
|
||||
}
|
||||
catch(NoSuchFieldException e)
|
||||
{
|
||||
@ -239,7 +413,7 @@ public abstract class QRecordEntity
|
||||
{
|
||||
if(method.getParameterTypes().length == 0 && method.getName().matches("^get[A-Z].*"))
|
||||
{
|
||||
if(isSupportedFieldType(method.getReturnType()))
|
||||
if(isSupportedFieldType(method.getReturnType()) || isSupportedAssociation(method.getReturnType(), method.getAnnotatedReturnType()))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
@ -304,4 +478,41 @@ public abstract class QRecordEntity
|
||||
/////////////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean isSupportedAssociation(Class<?> returnType, AnnotatedType annotatedType)
|
||||
{
|
||||
Class<?> listTypeParam = getListTypeParam(returnType, annotatedType);
|
||||
return (listTypeParam != null && QRecordEntity.class.isAssignableFrom(listTypeParam));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static Class<?> getListTypeParam(Class<?> listType, AnnotatedType annotatedType)
|
||||
{
|
||||
if(listType.equals(List.class))
|
||||
{
|
||||
if(annotatedType instanceof AnnotatedParameterizedType apt)
|
||||
{
|
||||
AnnotatedType[] annotatedActualTypeArguments = apt.getAnnotatedActualTypeArguments();
|
||||
for(AnnotatedType annotatedActualTypeArgument : annotatedActualTypeArguments)
|
||||
{
|
||||
Type type = annotatedActualTypeArgument.getType();
|
||||
if(type instanceof Class<?> c)
|
||||
{
|
||||
return (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.data;
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Reflective information about an association in a QRecordEntity
|
||||
*******************************************************************************/
|
||||
public class QRecordEntityAssociation
|
||||
{
|
||||
private final String fieldName;
|
||||
private final Method getter;
|
||||
private final Method setter;
|
||||
|
||||
private final Class<? extends QRecordEntity> associatedType;
|
||||
|
||||
private final QAssociation associationAnnotation;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor.
|
||||
*******************************************************************************/
|
||||
public QRecordEntityAssociation(String fieldName, Method getter, Method setter, Class<? extends QRecordEntity> associatedType, QAssociation associationAnnotation)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.associatedType = associatedType;
|
||||
this.associationAnnotation = associationAnnotation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for getter
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Method getGetter()
|
||||
{
|
||||
return getter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for setter
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Method getSetter()
|
||||
{
|
||||
return setter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for associatedType
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<? extends QRecordEntity> getAssociatedType()
|
||||
{
|
||||
return associatedType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for associationAnnotation
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAssociation getAssociationAnnotation()
|
||||
{
|
||||
return associationAnnotation;
|
||||
}
|
||||
|
||||
}
|
@ -22,7 +22,6 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
@ -43,12 +42,19 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
private String name;
|
||||
private String backendType;
|
||||
|
||||
private Boolean usesVariants = false;
|
||||
private String variantOptionsTableName;
|
||||
|
||||
private Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
private Set<Capability> disabledCapabilities = new HashSet<>();
|
||||
|
||||
private Boolean usesVariants = false;
|
||||
private String variantOptionsTableIdField;
|
||||
private String variantOptionsTableNameField;
|
||||
private String variantOptionsTableTypeField;
|
||||
private String variantOptionsTableTypeValue;
|
||||
private String variantOptionsTableUsernameField;
|
||||
private String variantOptionsTablePasswordField;
|
||||
private String variantOptionsTableApiKeyField;
|
||||
private String variantOptionsTableName;
|
||||
|
||||
// todo - at some point, we may want to apply this to secret properties on subclasses?
|
||||
// @JsonFilter("secretsFilter")
|
||||
|
||||
@ -59,6 +65,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData()
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// by default, we will turn off the query stats capability on all backends //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
withoutCapability(Capability.QUERY_STATS);
|
||||
}
|
||||
|
||||
|
||||
@ -199,6 +209,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
if(this.disabledCapabilities != null)
|
||||
{
|
||||
this.disabledCapabilities.removeAll(enabledCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -209,7 +223,7 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
setEnabledCapabilities(enabledCapabilities);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -221,7 +235,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
for(Capability enabledCapability : enabledCapabilities)
|
||||
{
|
||||
withCapability(enabledCapability);
|
||||
}
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -238,6 +255,7 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
this.disabledCapabilities.remove(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -249,11 +267,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
for(Capability enabledCapability : enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
withCapability(enabledCapability);
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -277,6 +294,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
if(this.enabledCapabilities != null)
|
||||
{
|
||||
this.enabledCapabilities.removeAll(disabledCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -287,7 +308,7 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
setDisabledCapabilities(disabledCapabilities);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -299,11 +320,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
for(Capability disabledCapability : disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
withoutCapability(disabledCapability);
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -315,7 +335,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
for(Capability disabledCapability : disabledCapabilities)
|
||||
{
|
||||
withCapability(disabledCapability);
|
||||
}
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -332,6 +355,7 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
this.enabledCapabilities.remove(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -381,7 +405,224 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantsOptionTableName
|
||||
** Getter for variantOptionsTableIdField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableIdField()
|
||||
{
|
||||
return (this.variantOptionsTableIdField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableIdField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableIdField(String variantOptionsTableIdField)
|
||||
{
|
||||
this.variantOptionsTableIdField = variantOptionsTableIdField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableIdField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableIdField(String variantOptionsTableIdField)
|
||||
{
|
||||
this.variantOptionsTableIdField = variantOptionsTableIdField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableNameField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableNameField()
|
||||
{
|
||||
return (this.variantOptionsTableNameField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableNameField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableNameField(String variantOptionsTableNameField)
|
||||
{
|
||||
this.variantOptionsTableNameField = variantOptionsTableNameField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableNameField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableNameField(String variantOptionsTableNameField)
|
||||
{
|
||||
this.variantOptionsTableNameField = variantOptionsTableNameField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableTypeField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableTypeField()
|
||||
{
|
||||
return (this.variantOptionsTableTypeField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableTypeField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableTypeField(String variantOptionsTableTypeField)
|
||||
{
|
||||
this.variantOptionsTableTypeField = variantOptionsTableTypeField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableTypeField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableTypeField(String variantOptionsTableTypeField)
|
||||
{
|
||||
this.variantOptionsTableTypeField = variantOptionsTableTypeField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableTypeValue
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableTypeValue()
|
||||
{
|
||||
return (this.variantOptionsTableTypeValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableTypeValue
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableTypeValue(String variantOptionsTableTypeValue)
|
||||
{
|
||||
this.variantOptionsTableTypeValue = variantOptionsTableTypeValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableTypeValue
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableTypeValue(String variantOptionsTableTypeValue)
|
||||
{
|
||||
this.variantOptionsTableTypeValue = variantOptionsTableTypeValue;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableUsernameField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableUsernameField()
|
||||
{
|
||||
return (this.variantOptionsTableUsernameField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableUsernameField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableUsernameField(String variantOptionsTableUsernameField)
|
||||
{
|
||||
this.variantOptionsTableUsernameField = variantOptionsTableUsernameField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableUsernameField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableUsernameField(String variantOptionsTableUsernameField)
|
||||
{
|
||||
this.variantOptionsTableUsernameField = variantOptionsTableUsernameField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTablePasswordField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTablePasswordField()
|
||||
{
|
||||
return (this.variantOptionsTablePasswordField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTablePasswordField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTablePasswordField(String variantOptionsTablePasswordField)
|
||||
{
|
||||
this.variantOptionsTablePasswordField = variantOptionsTablePasswordField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTablePasswordField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTablePasswordField(String variantOptionsTablePasswordField)
|
||||
{
|
||||
this.variantOptionsTablePasswordField = variantOptionsTablePasswordField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableApiKeyField
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableApiKeyField()
|
||||
{
|
||||
return (this.variantOptionsTableApiKeyField);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantOptionsTableApiKeyField
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField)
|
||||
{
|
||||
this.variantOptionsTableApiKeyField = variantOptionsTableApiKeyField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantOptionsTableApiKeyField
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField)
|
||||
{
|
||||
this.variantOptionsTableApiKeyField = variantOptionsTableApiKeyField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantOptionsTableName
|
||||
*******************************************************************************/
|
||||
public String getVariantOptionsTableName()
|
||||
{
|
||||
@ -391,7 +632,7 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantsOptionTableName
|
||||
** Setter for variantOptionsTableName
|
||||
*******************************************************************************/
|
||||
public void setVariantOptionsTableName(String variantOptionsTableName)
|
||||
{
|
||||
@ -401,11 +642,11 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantsOptionTableName
|
||||
** Fluent setter for variantOptionsTableName
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withVariantsOptionTableName(String variantsOptionTableName)
|
||||
public QBackendMetaData withVariantOptionsTableName(String variantOptionsTableName)
|
||||
{
|
||||
this.variantOptionsTableName = variantsOptionTableName;
|
||||
this.variantOptionsTableName = variantOptionsTableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,8 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.templates.RenderTemplateAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.RenderTemplateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.TemplateType;
|
||||
|
||||
|
||||
|
@ -72,6 +72,9 @@ public class QFrontendTableMetaData
|
||||
private boolean editPermission;
|
||||
private boolean deletePermission;
|
||||
|
||||
private boolean usesVariants;
|
||||
private String variantTableLabel;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -157,6 +160,13 @@ public class QFrontendTableMetaData
|
||||
insertPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.INSERT);
|
||||
editPermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.EDIT);
|
||||
deletePermission = PermissionsHelper.hasTablePermission(actionInput, tableMetaData.getName(), TablePermissionSubType.DELETE);
|
||||
|
||||
QBackendMetaData backend = actionInput.getInstance().getBackend(tableMetaData.getBackendName());
|
||||
if(backend != null && backend.getUsesVariants())
|
||||
{
|
||||
usesVariants = true;
|
||||
variantTableLabel = actionInput.getInstance().getTable(backend.getVariantOptionsTableName()).getLabel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -316,6 +326,17 @@ public class QFrontendTableMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for usesVariants
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getUsesVariants()
|
||||
{
|
||||
return usesVariants;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for exposedJoins
|
||||
**
|
||||
@ -335,4 +356,15 @@ public class QFrontendTableMetaData
|
||||
{
|
||||
return supplementalTableMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantTableLabel
|
||||
*******************************************************************************/
|
||||
public String getVariantTableLabel()
|
||||
{
|
||||
return (this.variantTableLabel);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.metadata.frontend;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Version of a variant for a frontend to see
|
||||
*******************************************************************************/
|
||||
public class QFrontendVariant
|
||||
{
|
||||
private Serializable id;
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Serializable getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Serializable id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QFrontendVariant withId(Serializable id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QFrontendVariant withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
*******************************************************************************/
|
||||
public String getType()
|
||||
{
|
||||
return (this.type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
*******************************************************************************/
|
||||
public void setType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QFrontendVariant withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -22,9 +22,6 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.scheduleing;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meta-data to define scheduled actions within QQQ.
|
||||
**
|
||||
@ -46,11 +43,8 @@ public class QScheduleMetaData
|
||||
private Integer initialDelaySeconds;
|
||||
private Integer initialDelayMillis;
|
||||
|
||||
private RunStrategy variantRunStrategy;
|
||||
private String backendVariant;
|
||||
private String variantTableName;
|
||||
private QQueryFilter variantFilter;
|
||||
private String variantFieldName;
|
||||
private RunStrategy variantRunStrategy;
|
||||
private String variantBackend;
|
||||
|
||||
|
||||
|
||||
@ -191,124 +185,31 @@ public class QScheduleMetaData
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendVariant
|
||||
** Getter for variantBackend
|
||||
*******************************************************************************/
|
||||
public String getBackendVariant()
|
||||
public String getVariantBackend()
|
||||
{
|
||||
return (this.backendVariant);
|
||||
return (this.variantBackend);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backendVariant
|
||||
** Setter for variantBackend
|
||||
*******************************************************************************/
|
||||
public void setBackendVariant(String backendVariant)
|
||||
public void setVariantBackend(String variantBackend)
|
||||
{
|
||||
this.backendVariant = backendVariant;
|
||||
this.variantBackend = variantBackend;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backendVariant
|
||||
** Fluent setter for variantBackend
|
||||
*******************************************************************************/
|
||||
public QScheduleMetaData withBackendVariant(String backendVariant)
|
||||
{
|
||||
this.backendVariant = backendVariant;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantTableName
|
||||
*******************************************************************************/
|
||||
public String getVariantTableName()
|
||||
{
|
||||
return (this.variantTableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantTableName
|
||||
*******************************************************************************/
|
||||
public void setVariantTableName(String variantTableName)
|
||||
{
|
||||
this.variantTableName = variantTableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantTableName
|
||||
*******************************************************************************/
|
||||
public QScheduleMetaData withVariantTableName(String variantTableName)
|
||||
{
|
||||
this.variantTableName = variantTableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantFilter
|
||||
*******************************************************************************/
|
||||
public QQueryFilter getVariantFilter()
|
||||
{
|
||||
return (this.variantFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantFilter
|
||||
*******************************************************************************/
|
||||
public void setVariantFilter(QQueryFilter variantFilter)
|
||||
{
|
||||
this.variantFilter = variantFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantFilter
|
||||
*******************************************************************************/
|
||||
public QScheduleMetaData withVariantFilter(QQueryFilter variantFilter)
|
||||
{
|
||||
this.variantFilter = variantFilter;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for variantFieldName
|
||||
*******************************************************************************/
|
||||
public String getVariantFieldName()
|
||||
{
|
||||
return (this.variantFieldName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for variantFieldName
|
||||
*******************************************************************************/
|
||||
public void setVariantFieldName(String variantFieldName)
|
||||
{
|
||||
this.variantFieldName = variantFieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for variantFieldName
|
||||
*******************************************************************************/
|
||||
public QScheduleMetaData withVariantFieldName(String variantFieldName)
|
||||
{
|
||||
this.variantFieldName = variantFieldName;
|
||||
this.variantBackend = backendVariant;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -33,9 +33,10 @@ public enum Capability
|
||||
TABLE_COUNT,
|
||||
TABLE_INSERT,
|
||||
TABLE_UPDATE,
|
||||
TABLE_DELETE
|
||||
TABLE_DELETE,
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with Capability.ts in qqq-frontend-core //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
QUERY_STATS
|
||||
}
|
||||
|
@ -862,6 +862,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
if(this.disabledCapabilities != null)
|
||||
{
|
||||
this.disabledCapabilities.removeAll(enabledCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -872,7 +876,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
setEnabledCapabilities(enabledCapabilities);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -884,7 +888,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
for(Capability enabledCapability : enabledCapabilities)
|
||||
{
|
||||
withCapability(enabledCapability);
|
||||
}
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -901,6 +908,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
this.disabledCapabilities.remove(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -912,11 +920,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
for(Capability enabledCapability : enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
withCapability(enabledCapability);
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -940,6 +947,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
if(this.enabledCapabilities != null)
|
||||
{
|
||||
this.enabledCapabilities.removeAll(disabledCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -950,7 +961,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
setDisabledCapabilities(disabledCapabilities);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -962,11 +973,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
for(Capability disabledCapability : disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
withoutCapability(disabledCapability);
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -978,7 +988,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
for(Capability disabledCapability : disabledCapabilities)
|
||||
{
|
||||
withCapability(disabledCapability);
|
||||
}
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -995,6 +1008,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
this.enabledCapabilities.remove(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* 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.querystats;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
|
||||
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.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QueryStat table
|
||||
*******************************************************************************/
|
||||
public class QueryStat extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "queryStat";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant startTimestamp;
|
||||
|
||||
@QField()
|
||||
private Instant firstResultTimestamp;
|
||||
|
||||
@QField()
|
||||
private Integer firstResultMillis;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
|
||||
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String action;
|
||||
|
||||
@QField(maxLength = 36, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String sessionId;
|
||||
|
||||
@QField(maxLength = 64 * 1024 - 1, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String queryText;
|
||||
|
||||
@QAssociation(name = "queryStatJoinTables")
|
||||
private List<QueryStatJoinTable> queryStatJoinTableList;
|
||||
|
||||
@QAssociation(name = "queryStatCriteriaFields")
|
||||
private List<QueryStatCriteriaField> queryStatCriteriaFieldList;
|
||||
|
||||
@QAssociation(name = "queryStatOrderByFields")
|
||||
private List<QueryStatOrderByField> queryStatOrderByFieldList;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// non-persistent fields - used to help build the record //
|
||||
///////////////////////////////////////////////////////////
|
||||
private String tableName;
|
||||
private Set<String> joinTableNames;
|
||||
private QQueryFilter queryFilter;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QueryStat()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QueryStat(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QueryStat withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for startTimestamp
|
||||
*******************************************************************************/
|
||||
public Instant getStartTimestamp()
|
||||
{
|
||||
return (this.startTimestamp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for startTimestamp
|
||||
*******************************************************************************/
|
||||
public void setStartTimestamp(Instant startTimestamp)
|
||||
{
|
||||
this.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for startTimestamp
|
||||
*******************************************************************************/
|
||||
public QueryStat withStartTimestamp(Instant startTimestamp)
|
||||
{
|
||||
this.startTimestamp = startTimestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for firstResultTimestamp
|
||||
*******************************************************************************/
|
||||
public Instant getFirstResultTimestamp()
|
||||
{
|
||||
return (this.firstResultTimestamp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for firstResultTimestamp
|
||||
*******************************************************************************/
|
||||
public void setFirstResultTimestamp(Instant firstResultTimestamp)
|
||||
{
|
||||
this.firstResultTimestamp = firstResultTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for firstResultTimestamp
|
||||
*******************************************************************************/
|
||||
public QueryStat withFirstResultTimestamp(Instant firstResultTimestamp)
|
||||
{
|
||||
this.firstResultTimestamp = firstResultTimestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for firstResultMillis
|
||||
*******************************************************************************/
|
||||
public Integer getFirstResultMillis()
|
||||
{
|
||||
return (this.firstResultMillis);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for firstResultMillis
|
||||
*******************************************************************************/
|
||||
public void setFirstResultMillis(Integer firstResultMillis)
|
||||
{
|
||||
this.firstResultMillis = firstResultMillis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for firstResultMillis
|
||||
*******************************************************************************/
|
||||
public QueryStat withFirstResultMillis(Integer firstResultMillis)
|
||||
{
|
||||
this.firstResultMillis = firstResultMillis;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryText
|
||||
*******************************************************************************/
|
||||
public String getQueryText()
|
||||
{
|
||||
return (this.queryText);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryText
|
||||
*******************************************************************************/
|
||||
public void setQueryText(String queryText)
|
||||
{
|
||||
this.queryText = queryText;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryText
|
||||
*******************************************************************************/
|
||||
public QueryStat withQueryText(String queryText)
|
||||
{
|
||||
this.queryText = queryText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatJoinTableList
|
||||
*******************************************************************************/
|
||||
public List<QueryStatJoinTable> getQueryStatJoinTableList()
|
||||
{
|
||||
return (this.queryStatJoinTableList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatJoinTableList
|
||||
*******************************************************************************/
|
||||
public void setQueryStatJoinTableList(List<QueryStatJoinTable> queryStatJoinTableList)
|
||||
{
|
||||
this.queryStatJoinTableList = queryStatJoinTableList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatJoinTableList
|
||||
*******************************************************************************/
|
||||
public QueryStat withQueryStatJoinTableList(List<QueryStatJoinTable> queryStatJoinTableList)
|
||||
{
|
||||
this.queryStatJoinTableList = queryStatJoinTableList;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatCriteriaFieldList
|
||||
*******************************************************************************/
|
||||
public List<QueryStatCriteriaField> getQueryStatCriteriaFieldList()
|
||||
{
|
||||
return (this.queryStatCriteriaFieldList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatCriteriaFieldList
|
||||
*******************************************************************************/
|
||||
public void setQueryStatCriteriaFieldList(List<QueryStatCriteriaField> queryStatCriteriaFieldList)
|
||||
{
|
||||
this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatCriteriaFieldList
|
||||
*******************************************************************************/
|
||||
public QueryStat withQueryStatCriteriaFieldList(List<QueryStatCriteriaField> queryStatCriteriaFieldList)
|
||||
{
|
||||
this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatOrderByFieldList
|
||||
*******************************************************************************/
|
||||
public List<QueryStatOrderByField> getQueryStatOrderByFieldList()
|
||||
{
|
||||
return (this.queryStatOrderByFieldList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatOrderByFieldList
|
||||
*******************************************************************************/
|
||||
public void setQueryStatOrderByFieldList(List<QueryStatOrderByField> queryStatOrderByFieldList)
|
||||
{
|
||||
this.queryStatOrderByFieldList = queryStatOrderByFieldList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatOrderByFieldList
|
||||
*******************************************************************************/
|
||||
public QueryStat withQueryStatOrderByFieldList(List<QueryStatOrderByField> queryStatOrderByFieldList)
|
||||
{
|
||||
this.queryStatOrderByFieldList = queryStatOrderByFieldList;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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 QueryStat withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryFilter
|
||||
*******************************************************************************/
|
||||
public QQueryFilter getQueryFilter()
|
||||
{
|
||||
return (this.queryFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryFilter
|
||||
*******************************************************************************/
|
||||
public void setQueryFilter(QQueryFilter queryFilter)
|
||||
{
|
||||
this.queryFilter = queryFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryFilter
|
||||
*******************************************************************************/
|
||||
public QueryStat withQueryFilter(QQueryFilter queryFilter)
|
||||
{
|
||||
this.queryFilter = queryFilter;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public QueryStat withQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for joinTableNames
|
||||
*******************************************************************************/
|
||||
public Set<String> getJoinTableNames()
|
||||
{
|
||||
return (this.joinTableNames);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for joinTableNames
|
||||
*******************************************************************************/
|
||||
public void setJoinTableNames(Set<String> joinTableNames)
|
||||
{
|
||||
this.joinTableNames = joinTableNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for joinTableNames
|
||||
*******************************************************************************/
|
||||
public QueryStat withJoinTableNames(Set<String> joinTableNames)
|
||||
{
|
||||
this.joinTableNames = joinTableNames;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for action
|
||||
*******************************************************************************/
|
||||
public String getAction()
|
||||
{
|
||||
return (this.action);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for action
|
||||
*******************************************************************************/
|
||||
public void setAction(String action)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for action
|
||||
*******************************************************************************/
|
||||
public QueryStat withAction(String action)
|
||||
{
|
||||
this.action = action;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sessionId
|
||||
*******************************************************************************/
|
||||
public String getSessionId()
|
||||
{
|
||||
return (this.sessionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sessionId
|
||||
*******************************************************************************/
|
||||
public void setSessionId(String sessionId)
|
||||
{
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sessionId
|
||||
*******************************************************************************/
|
||||
public QueryStat withSessionId(String sessionId)
|
||||
{
|
||||
this.sessionId = sessionId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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.querystats;
|
||||
|
||||
|
||||
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.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QueryStatCriteriaField table
|
||||
*******************************************************************************/
|
||||
public class QueryStatCriteriaField extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "queryStatCriteriaField";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String name;
|
||||
|
||||
@QField(maxLength = 30, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String operator;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String values;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatId
|
||||
*******************************************************************************/
|
||||
public Integer getQueryStatId()
|
||||
{
|
||||
return (this.queryStatId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public void setQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for operator
|
||||
*******************************************************************************/
|
||||
public String getOperator()
|
||||
{
|
||||
return (this.operator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for operator
|
||||
*******************************************************************************/
|
||||
public void setOperator(String operator)
|
||||
{
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for operator
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withOperator(String operator)
|
||||
{
|
||||
this.operator = operator;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for values
|
||||
*******************************************************************************/
|
||||
public String getValues()
|
||||
{
|
||||
return (this.values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for values
|
||||
*******************************************************************************/
|
||||
public void setValues(String values)
|
||||
{
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for values
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withValues(String values)
|
||||
{
|
||||
this.values = values;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.querystats;
|
||||
|
||||
|
||||
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.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QueryStatJoinTable table
|
||||
*******************************************************************************/
|
||||
public class QueryStatJoinTable extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "queryStatJoinTable"; // todo - lowercase the first letter
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
|
||||
@QField(maxLength = 10, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String type;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatId
|
||||
*******************************************************************************/
|
||||
public Integer getQueryStatId()
|
||||
{
|
||||
return (this.queryStatId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public void setQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable withQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable withQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
*******************************************************************************/
|
||||
public String getType()
|
||||
{
|
||||
return (this.type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for type
|
||||
*******************************************************************************/
|
||||
public void setType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for type
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable withType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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.querystats;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
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.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QueryStatMetaDataProvider
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
addJoins(instance);
|
||||
|
||||
defineQueryStatTable(instance, backendName, backendDetailEnricher);
|
||||
|
||||
instance.addTable(defineStandardTable(QueryStatJoinTable.TABLE_NAME, QueryStatJoinTable.class, backendName, backendDetailEnricher));
|
||||
|
||||
instance.addTable(defineStandardTable(QueryStatCriteriaField.TABLE_NAME, QueryStatCriteriaField.class, backendName, backendDetailEnricher)
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable(QueryStat.TABLE_NAME))
|
||||
);
|
||||
|
||||
instance.addTable(defineStandardTable(QueryStatOrderByField.TABLE_NAME, QueryStatOrderByField.class, backendName, backendDetailEnricher));
|
||||
|
||||
instance.addPossibleValueSource(defineQueryStatPossibleValueSource());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void addJoins(QInstance instance)
|
||||
{
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withLeftTable(QueryStat.TABLE_NAME)
|
||||
.withRightTable(QueryStatJoinTable.TABLE_NAME)
|
||||
.withInferredName()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withLeftTable(QueryStat.TABLE_NAME)
|
||||
.withRightTable(QueryStatCriteriaField.TABLE_NAME)
|
||||
.withInferredName()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withLeftTable(QueryStat.TABLE_NAME)
|
||||
.withRightTable(QueryStatOrderByField.TABLE_NAME)
|
||||
.withInferredName()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineQueryStatTable(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
String joinTablesJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatJoinTable.TABLE_NAME);
|
||||
String criteriaFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatCriteriaField.TABLE_NAME);
|
||||
String orderByFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatOrderByField.TABLE_NAME);
|
||||
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(QueryStat.TABLE_NAME)
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.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("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"))
|
||||
.withSection(new QFieldSection("orderBys", new QIcon().withName("sort_by_alpha"), Tier.T2).withWidgetName(orderByFieldsJoinName + "Widget"))
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(joinTablesJoinName)).withName(joinTablesJoinName + "Widget").withLabel("Join Tables").getWidgetMetaData());
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(criteriaFieldsJoinName)).withName(criteriaFieldsJoinName + "Widget").withLabel("Criteria Fields").getWidgetMetaData());
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(orderByFieldsJoinName)).withName(orderByFieldsJoinName + "Widget").withLabel("Order by Fields").getWidgetMetaData());
|
||||
|
||||
table.withExposedJoin(new ExposedJoin().withJoinTable(QueryStatCriteriaField.TABLE_NAME));
|
||||
table.withExposedJoin(new ExposedJoin().withJoinTable(QueryStatJoinTable.TABLE_NAME));
|
||||
table.withExposedJoin(new ExposedJoin().withJoinTable(QueryStatOrderByField.TABLE_NAME));
|
||||
|
||||
table.withAssociation(new Association().withName("queryStatJoinTables").withJoinName(joinTablesJoinName).withAssociatedTableName(QueryStatJoinTable.TABLE_NAME))
|
||||
.withAssociation(new Association().withName("queryStatCriteriaFields").withJoinName(criteriaFieldsJoinName).withAssociatedTableName(QueryStatCriteriaField.TABLE_NAME))
|
||||
.withAssociation(new Association().withName("queryStatOrderByFields").withJoinName(orderByFieldsJoinName).withAssociatedTableName(QueryStatOrderByField.TABLE_NAME));
|
||||
|
||||
table.getField("queryText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("sql")));
|
||||
table.getField("firstResultMillis").withDisplayFormat(DisplayFormat.COMMAS);
|
||||
|
||||
instance.addTable(table);
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineStandardTable(String tableName, Class<? extends QRecordEntity> entityClass, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(tableName)
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%d")
|
||||
.withRecordLabelFields("id")
|
||||
.withPrimaryKeyField("id")
|
||||
.withFieldsFromEntity(entityClass)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource defineQueryStatPossibleValueSource()
|
||||
{
|
||||
return (new QPossibleValueSource()
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withName(QueryStat.TABLE_NAME)
|
||||
.withTableName(QueryStat.TABLE_NAME));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.querystats;
|
||||
|
||||
|
||||
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.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QueryStatOrderByField table
|
||||
*******************************************************************************/
|
||||
public class QueryStatOrderByField extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "queryStatOrderByField";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String name;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for queryStatId
|
||||
*******************************************************************************/
|
||||
public Integer getQueryStatId()
|
||||
{
|
||||
return (this.queryStatId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public void setQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for queryStatId
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField withQueryStatId(Integer queryStatId)
|
||||
{
|
||||
this.queryStatId = queryStatId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField withQqqTableId(Integer qqqTableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,9 @@ package com.kingsrook.qqq.backend.core.model.scripts;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
|
||||
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;
|
||||
@ -55,9 +57,6 @@ public class ScriptRevision extends QRecordEntity
|
||||
@QField(possibleValueSourceName = "apiName", label = "API Name")
|
||||
private String apiName;
|
||||
|
||||
@QField()
|
||||
private String contents;
|
||||
|
||||
@QField()
|
||||
private Integer sequenceNo;
|
||||
|
||||
@ -67,6 +66,9 @@ public class ScriptRevision extends QRecordEntity
|
||||
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String author;
|
||||
|
||||
@QAssociation(name = "files")
|
||||
private List<ScriptRevisionFile> files;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -226,40 +228,6 @@ public class ScriptRevision extends QRecordEntity
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for contents
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getContents()
|
||||
{
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for contents
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setContents(String contents)
|
||||
{
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for contents
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withContents(String contents)
|
||||
{
|
||||
this.contents = contents;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sequenceNo
|
||||
**
|
||||
@ -422,4 +390,35 @@ public class ScriptRevision extends QRecordEntity
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for files
|
||||
*******************************************************************************/
|
||||
public List<ScriptRevisionFile> getFiles()
|
||||
{
|
||||
return (this.files);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for files
|
||||
*******************************************************************************/
|
||||
public void setFiles(List<ScriptRevisionFile> files)
|
||||
{
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for files
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withFiles(List<ScriptRevisionFile> files)
|
||||
{
|
||||
this.files = files;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.scripts;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
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.fields.ValueTooLongBehavior;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptRevisionFile extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptRevisionFile";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant createDate;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptRevision")
|
||||
private Integer scriptRevisionId;
|
||||
|
||||
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||
private String fileName;
|
||||
|
||||
@QField()
|
||||
private String contents;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptRevisionId
|
||||
*******************************************************************************/
|
||||
public Integer getScriptRevisionId()
|
||||
{
|
||||
return (this.scriptRevisionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptRevisionId
|
||||
*******************************************************************************/
|
||||
public void setScriptRevisionId(Integer scriptRevisionId)
|
||||
{
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptRevisionId
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withScriptRevisionId(Integer scriptRevisionId)
|
||||
{
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fileName
|
||||
*******************************************************************************/
|
||||
public String getFileName()
|
||||
{
|
||||
return (this.fileName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fileName
|
||||
*******************************************************************************/
|
||||
public void setFileName(String fileName)
|
||||
{
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fileName
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withFileName(String fileName)
|
||||
{
|
||||
this.fileName = fileName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for contents
|
||||
*******************************************************************************/
|
||||
public String getContents()
|
||||
{
|
||||
return (this.contents);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for contents
|
||||
*******************************************************************************/
|
||||
public void setContents(String contents)
|
||||
{
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for contents
|
||||
*******************************************************************************/
|
||||
public ScriptRevisionFile withContents(String contents)
|
||||
{
|
||||
this.contents = contents;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.scripts;
|
||||
|
||||
import java.time.Instant;
|
||||
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;
|
||||
|
||||
|
||||
@ -52,6 +53,33 @@ public class ScriptType extends QRecordEntity
|
||||
@QField()
|
||||
private String sampleCode;
|
||||
|
||||
@QField(possibleValueSourceName = ScriptTypeFileMode.NAME)
|
||||
private Integer fileMode;
|
||||
|
||||
@QField()
|
||||
private String testScriptInterfaceName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType(QRecord qRecord)
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -256,4 +284,66 @@ public class ScriptType extends QRecordEntity
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fileMode
|
||||
*******************************************************************************/
|
||||
public Integer getFileMode()
|
||||
{
|
||||
return (this.fileMode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fileMode
|
||||
*******************************************************************************/
|
||||
public void setFileMode(Integer fileMode)
|
||||
{
|
||||
this.fileMode = fileMode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fileMode
|
||||
*******************************************************************************/
|
||||
public ScriptType withFileMode(Integer fileMode)
|
||||
{
|
||||
this.fileMode = fileMode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for testScriptInterfaceName
|
||||
*******************************************************************************/
|
||||
public String getTestScriptInterfaceName()
|
||||
{
|
||||
return (this.testScriptInterfaceName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for testScriptInterfaceName
|
||||
*******************************************************************************/
|
||||
public void setTestScriptInterfaceName(String testScriptInterfaceName)
|
||||
{
|
||||
this.testScriptInterfaceName = testScriptInterfaceName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for testScriptInterfaceName
|
||||
*******************************************************************************/
|
||||
public ScriptType withTestScriptInterfaceName(String testScriptInterfaceName)
|
||||
{
|
||||
this.testScriptInterfaceName = testScriptInterfaceName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.scripts;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** ScriptTypeFileMode - possible value enum
|
||||
*******************************************************************************/
|
||||
public enum ScriptTypeFileMode implements PossibleValueEnum<Integer>
|
||||
{
|
||||
SINGLE(1, "Single File"),
|
||||
MULTI_PRE_DEFINED(2, "Multi File (Pre-defined)"),
|
||||
MULTI_AD_HOC(3, "Multi File (ad hoc)");
|
||||
|
||||
private final Integer id;
|
||||
private final String label;
|
||||
|
||||
public static final String NAME = "scriptTypeFileMode";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
ScriptTypeFileMode(Integer id, String label)
|
||||
{
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Integer getPossibleValueId()
|
||||
{
|
||||
return (getId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
return (getLabel());
|
||||
}
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.scripts;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
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.fields.ValueTooLongBehavior;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptTypeFileSchema extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptTypeFileSchema";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant createDate;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptType")
|
||||
private Integer scriptTypeId;
|
||||
|
||||
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||
private String name;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||
private String fileType;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptTypeId
|
||||
*******************************************************************************/
|
||||
public Integer getScriptTypeId()
|
||||
{
|
||||
return (this.scriptTypeId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptTypeId
|
||||
*******************************************************************************/
|
||||
public void setScriptTypeId(Integer scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptTypeId
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withScriptTypeId(Integer scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fileType
|
||||
*******************************************************************************/
|
||||
public String getFileType()
|
||||
{
|
||||
return (this.fileType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fileType
|
||||
*******************************************************************************/
|
||||
public void setFileType(String fileType)
|
||||
{
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fileType
|
||||
*******************************************************************************/
|
||||
public ScriptTypeFileSchema withFileType(String fileType)
|
||||
{
|
||||
this.fileType = fileType;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -46,18 +46,22 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
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.TablesPossibleValueSourceMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||
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.RunRecordScriptTransformStep;
|
||||
@ -70,12 +74,15 @@ import com.kingsrook.qqq.backend.core.processes.implementations.scripts.TestScri
|
||||
*******************************************************************************/
|
||||
public class ScriptsMetaDataProvider
|
||||
{
|
||||
public static final String RUN_RECORD_SCRIPT_PROCESS_NAME = "runRecordScript";
|
||||
public static final String STORE_SCRIPT_REVISION_PROCESS_NAME = "storeScriptRevision";
|
||||
public static final String TEST_SCRIPT_PROCESS_NAME = "testScript";
|
||||
public static final String RUN_RECORD_SCRIPT_PROCESS_NAME = "runRecordScript";
|
||||
public static final String STORE_SCRIPT_REVISION_PROCESS_NAME = "storeScriptRevision";
|
||||
public static final String TEST_SCRIPT_PROCESS_NAME = "testScript";
|
||||
public static final String LOAD_SCRIPT_TEST_DETAILS_PROCESS_NAME = "loadScriptTestDetails";
|
||||
|
||||
public static final String SCRIPT_TYPE_NAME_RECORD = "Record Script";
|
||||
|
||||
public static final String CURRENT_SCRIPT_REVISION_JOIN_NAME = "currentScriptRevision";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -90,11 +97,30 @@ public class ScriptsMetaDataProvider
|
||||
instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance));
|
||||
instance.addProcess(defineStoreScriptRevisionProcess());
|
||||
instance.addProcess(defineTestScriptProcess());
|
||||
instance.addProcess(defineLoadScriptTestDetailsProcess());
|
||||
instance.addProcess(defineRunRecordScriptProcess());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QProcessMetaData defineLoadScriptTestDetailsProcess()
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName(LOAD_SCRIPT_TEST_DETAILS_PROCESS_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
.withIsHidden(true)
|
||||
//? .withPermissionRules(new QPermissionRules().withLevel(PermissionLevel.NOT_PROTECTED))
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withName("main")
|
||||
.withCode(new QCodeReference(LoadScriptTestDetailsProcessStep.class)))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -173,6 +199,14 @@ public class ScriptsMetaDataProvider
|
||||
.withLabel("Recent Logs")
|
||||
.getWidgetMetaData());
|
||||
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME)))
|
||||
.withLabel("File Schema")
|
||||
.getWidgetMetaData());
|
||||
|
||||
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME))).withMaxRows(50)
|
||||
.withLabel("Files")
|
||||
.getWidgetMetaData());
|
||||
|
||||
instance.addWidget(new QWidgetMetaData()
|
||||
.withName("scriptViewer")
|
||||
.withLabel("Contents")
|
||||
@ -209,7 +243,7 @@ public class ScriptsMetaDataProvider
|
||||
.withLeftTable(Script.TABLE_NAME)
|
||||
.withRightTable(ScriptRevision.TABLE_NAME)
|
||||
.withJoinOn(new JoinOn("currentScriptRevisionId", "id"))
|
||||
.withName("currentScriptRevision"));
|
||||
.withName(CURRENT_SCRIPT_REVISION_JOIN_NAME));
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
@ -227,6 +261,22 @@ public class ScriptsMetaDataProvider
|
||||
.withOrderBy(new QFilterOrderBy("id"))
|
||||
.withInferredName());
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withLeftTable(ScriptType.TABLE_NAME)
|
||||
.withRightTable(ScriptTypeFileSchema.TABLE_NAME)
|
||||
.withJoinOn(new JoinOn("id", "scriptTypeId"))
|
||||
.withOrderBy(new QFilterOrderBy("id"))
|
||||
.withInferredName());
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withLeftTable(ScriptRevision.TABLE_NAME)
|
||||
.withRightTable(ScriptRevisionFile.TABLE_NAME)
|
||||
.withJoinOn(new JoinOn("id", "scriptRevisionId"))
|
||||
.withOrderBy(new QFilterOrderBy("id"))
|
||||
.withInferredName());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -251,23 +301,25 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(Script.TABLE_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
);
|
||||
.withTableName(Script.TABLE_NAME));
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptRevision.TABLE_NAME)
|
||||
.withTableName(ScriptRevision.TABLE_NAME)
|
||||
);
|
||||
.withTableName(ScriptRevision.TABLE_NAME));
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptType.TABLE_NAME)
|
||||
.withTableName(ScriptType.TABLE_NAME)
|
||||
);
|
||||
.withTableName(ScriptType.TABLE_NAME));
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptLog.TABLE_NAME)
|
||||
.withTableName(ScriptLog.TABLE_NAME)
|
||||
);
|
||||
.withTableName(ScriptLog.TABLE_NAME));
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptTypeFileMode.NAME)
|
||||
.withType(QPossibleValueSourceType.ENUM)
|
||||
.withValuesFromEnum(ScriptTypeFileMode.values())
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY));
|
||||
}
|
||||
|
||||
|
||||
@ -279,8 +331,10 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
List<QTableMetaData> rs = new ArrayList<>();
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTypeFileSchemaTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptRevisionTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptRevisionFileTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptLogTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptLogLineTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineTableTriggerTable(backendName)));
|
||||
@ -372,7 +426,8 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptType.TABLE_NAME, ScriptType.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name")))
|
||||
.withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode")))
|
||||
.withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("helpText", "sampleCode", "fileMode", "testScriptInterfaceName")))
|
||||
.withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptType.TABLE_NAME, ScriptTypeFileSchema.TABLE_NAME)))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript")));
|
||||
tableMetaData.getField("helpText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("text")));
|
||||
@ -381,6 +436,22 @@ public class ScriptsMetaDataProvider
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptTypeFileSchemaTable(String backendName) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptTypeFileSchema.TABLE_NAME, ScriptTypeFileSchema.class)
|
||||
.withRecordLabelFormat("%s - %s")
|
||||
.withRecordLabelFields(List.of("scriptTypeId", "name"))
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId")))
|
||||
.withSection(new QFieldSection("details", new QIcon().withName("dataset"), Tier.T2, List.of("fileType")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -391,11 +462,15 @@ public class ScriptsMetaDataProvider
|
||||
.withRecordLabelFormat("%s v%s")
|
||||
.withRecordLabelFields(List.of("scriptId", "sequenceNo"))
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptId", "sequenceNo")))
|
||||
.withSection(new QFieldSection("code", new QIcon().withName("data_object"), Tier.T2, List.of("contents")))
|
||||
.withSection(new QFieldSection("files", new QIcon().withName("description"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME)))
|
||||
.withSection(new QFieldSection("changeManagement", new QIcon().withName("history"), Tier.T2, List.of("commitMessage", "author")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
|
||||
|
||||
.withAssociation(new Association()
|
||||
.withName("files")
|
||||
.withAssociatedTableName(ScriptRevisionFile.TABLE_NAME)
|
||||
.withJoinName(QJoinMetaData.makeInferredJoinName(ScriptRevision.TABLE_NAME, ScriptRevisionFile.TABLE_NAME)));
|
||||
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("javascript")));
|
||||
tableMetaData.getField("scriptId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
|
||||
|
||||
try
|
||||
@ -420,6 +495,27 @@ public class ScriptsMetaDataProvider
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptRevisionFileTable(String backendName) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptRevisionFile.TABLE_NAME, ScriptRevisionFile.class)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE)
|
||||
.withRecordLabelFormat("%s - %s")
|
||||
.withRecordLabelFields(List.of("scriptRevisionId", "fileName"))
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "scriptRevisionId", "fileName")))
|
||||
.withSection(new QFieldSection("code", new QIcon().withName("data_object"), Tier.T2, List.of("contents")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("velocity"))); // todo - dynamic?!
|
||||
tableMetaData.getField("scriptRevisionId").withFieldAdornment(AdornmentType.Size.LARGE.toAdornment());
|
||||
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.time.Instant;
|
||||
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.fields.ValueTooLongBehavior;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** QRecord Entity for QQQTable table
|
||||
*******************************************************************************/
|
||||
public class QQQTable extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "qqqTable";
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Integer id;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant createDate;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||
private String name;
|
||||
|
||||
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String label;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Default constructor
|
||||
*******************************************************************************/
|
||||
public QQQTable()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor that takes a QRecord
|
||||
*******************************************************************************/
|
||||
public QQQTable(QRecord record)
|
||||
{
|
||||
populateFromQRecord(record);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return (this.id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
*******************************************************************************/
|
||||
public QQQTable withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return (this.createDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
*******************************************************************************/
|
||||
public QQQTable withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return (this.modifyDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
*******************************************************************************/
|
||||
public QQQTable withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return (this.name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
*******************************************************************************/
|
||||
public QQQTable withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return (this.label);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for label
|
||||
*******************************************************************************/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for label
|
||||
*******************************************************************************/
|
||||
public QQQTable withLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
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.metadata.tables.cache.CacheOf;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QQQTablesMetaDataProvider
|
||||
{
|
||||
public static final String QQQ_TABLE_CACHE_TABLE_NAME = QQQTable.TABLE_NAME + "Cache";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData defineQQQTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(QQQTable.TABLE_NAME)
|
||||
.withLabel("Table")
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("name"))
|
||||
.withFieldsFromEntity(QQQTable.class)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData defineQQQTableCache(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName(QQQ_TABLE_CACHE_TABLE_NAME)
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("name"))
|
||||
.withFieldsFromEntity(QQQTable.class)
|
||||
.withCacheOf(new CacheOf()
|
||||
.withSourceTable(QQQTable.TABLE_NAME)
|
||||
.withUseCase(new CacheUseCase()
|
||||
.withType(CacheUseCase.Type.UNIQUE_KEY_TO_UNIQUE_KEY)
|
||||
.withCacheSourceMisses(false)
|
||||
.withCacheUniqueKey(new UniqueKey("name"))
|
||||
.withSourceUniqueKey(new UniqueKey("name"))
|
||||
)
|
||||
);
|
||||
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource defineQQQTablePossibleValueSource()
|
||||
{
|
||||
return (new QPossibleValueSource()
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withName(QQQTable.TABLE_NAME)
|
||||
.withTableName(QQQTable.TABLE_NAME));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to load the details necessary to test a script.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class LoadScriptTestDetailsProcessStep implements BackendStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
Integer scriptTypeId = input.getValueInteger("scriptTypeId");
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(ScriptType.TABLE_NAME);
|
||||
getInput.setPrimaryKey(scriptTypeId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
ScriptType scriptType = new ScriptType(getOutput.getRecord());
|
||||
|
||||
TestScriptActionInterface testScriptActionInterface = QCodeLoader.getAdHoc(TestScriptActionInterface.class, new QCodeReference(scriptType.getTestScriptInterfaceName(), QCodeType.JAVA));
|
||||
|
||||
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(new QInstance());
|
||||
|
||||
ArrayList<QFieldMetaData> inputFields = new ArrayList<>();
|
||||
for(QFieldMetaData testInputField : CollectionUtils.nonNullList(testScriptActionInterface.getTestInputFields()))
|
||||
{
|
||||
qInstanceEnricher.enrichField(testInputField);
|
||||
inputFields.add(testInputField);
|
||||
}
|
||||
|
||||
ArrayList<QFieldMetaData> outputFields = new ArrayList<>();
|
||||
for(QFieldMetaData testOutputField : CollectionUtils.nonNullList(testScriptActionInterface.getTestOutputFields()))
|
||||
{
|
||||
qInstanceEnricher.enrichField(testOutputField);
|
||||
outputFields.add(testOutputField);
|
||||
}
|
||||
|
||||
output.addValue("testInputFields", inputFields);
|
||||
output.addValue("testOutputFields", outputFields);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
output.addValue("exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,10 @@
|
||||
package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
@ -44,6 +46,8 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
@ -63,87 +67,133 @@ public class StoreScriptRevisionProcessStep implements BackendStep
|
||||
@Override
|
||||
public void run(RunBackendStepInput input, RunBackendStepOutput output) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// check if there's currently a script referenced by the record //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
Integer scriptId = input.getValueInteger("scriptId");
|
||||
Integer nextSequenceNo = 1;
|
||||
|
||||
////////////////////////////////////////
|
||||
// get the existing script, to update //
|
||||
////////////////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName("script");
|
||||
getInput.setPrimaryKey(scriptId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
QRecord script = getOutput.getRecord();
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName("scriptRevision");
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(script.getValue("id"))))
|
||||
.withOrderBy(new QFilterOrderBy("sequenceNo", false))
|
||||
.withLimit(1));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
if(!queryOutput.getRecords().isEmpty())
|
||||
{
|
||||
nextSequenceNo = queryOutput.getRecords().get(0).getValueInteger("sequenceNo") + 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// insert a new script revision //
|
||||
//////////////////////////////////
|
||||
String commitMessage = input.getValueString("commitMessage");
|
||||
if(!StringUtils.hasContent(commitMessage))
|
||||
{
|
||||
if(nextSequenceNo == 1)
|
||||
{
|
||||
commitMessage = "Initial version";
|
||||
}
|
||||
else
|
||||
{
|
||||
commitMessage = "No commit message given";
|
||||
}
|
||||
}
|
||||
|
||||
QRecord scriptRevision = new QRecord()
|
||||
.withValue("scriptId", script.getValue("id"))
|
||||
.withValue("contents", input.getValueString("contents"))
|
||||
.withValue("apiName", input.getValueString("apiName"))
|
||||
.withValue("apiVersion", input.getValueString("apiVersion"))
|
||||
.withValue("commitMessage", commitMessage)
|
||||
.withValue("sequenceNo", nextSequenceNo);
|
||||
InsertAction insertAction = new InsertAction();
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName("scriptRevision");
|
||||
QBackendTransaction transaction = insertAction.openTransaction(insertInput);
|
||||
insertInput.setTransaction(transaction);
|
||||
|
||||
try
|
||||
{
|
||||
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// check if there's currently a script referenced by the record //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
Integer scriptId = input.getValueInteger("scriptId");
|
||||
Integer nextSequenceNo = 1;
|
||||
|
||||
////////////////////////////////////////
|
||||
// get the existing script, to update //
|
||||
////////////////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName("script");
|
||||
getInput.setPrimaryKey(scriptId);
|
||||
getInput.setTransaction(transaction);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
QRecord script = getOutput.getRecord();
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName("scriptRevision");
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(script.getValue("id"))))
|
||||
.withOrderBy(new QFilterOrderBy("sequenceNo", false))
|
||||
.withLimit(1));
|
||||
queryInput.setTransaction(transaction);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
if(!queryOutput.getRecords().isEmpty())
|
||||
{
|
||||
nextSequenceNo = queryOutput.getRecords().get(0).getValueInteger("sequenceNo") + 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
// insert a new script revision //
|
||||
//////////////////////////////////
|
||||
String commitMessage = input.getValueString("commitMessage");
|
||||
if(!StringUtils.hasContent(commitMessage))
|
||||
{
|
||||
if(nextSequenceNo == 1)
|
||||
{
|
||||
commitMessage = "Initial version";
|
||||
}
|
||||
else
|
||||
{
|
||||
commitMessage = "No commit message given";
|
||||
}
|
||||
}
|
||||
|
||||
QRecord scriptRevision = new QRecord()
|
||||
.withValue("scriptId", script.getValue("id"))
|
||||
.withValue("apiName", input.getValueString("apiName"))
|
||||
.withValue("apiVersion", input.getValueString("apiVersion"))
|
||||
.withValue("commitMessage", commitMessage)
|
||||
.withValue("sequenceNo", nextSequenceNo);
|
||||
|
||||
try
|
||||
{
|
||||
scriptRevision.setValue("author", input.getSession().getUser().getFullName());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
scriptRevision.setValue("author", "Unknown");
|
||||
}
|
||||
|
||||
insertInput.setRecords(List.of(scriptRevision));
|
||||
InsertOutput insertOutput = insertAction.execute(insertInput);
|
||||
scriptRevision = insertOutput.getRecords().get(0);
|
||||
Integer scriptRevisionId = scriptRevision.getValueInteger("id");
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Store the file(s) under the revision //
|
||||
//////////////////////////////////////////
|
||||
List<QRecord> scriptRevisionFileRecords = null;
|
||||
if(StringUtils.hasContent(input.getValueString("fileNames")))
|
||||
{
|
||||
scriptRevisionFileRecords = new ArrayList<>();
|
||||
for(String fileName : input.getValueString("fileNames").split(","))
|
||||
{
|
||||
scriptRevisionFileRecords.add(new ScriptRevisionFile()
|
||||
.withScriptRevisionId(scriptRevisionId)
|
||||
.withFileName(fileName)
|
||||
.withContents(input.getValueString("fileContents:" + fileName))
|
||||
.toQRecord());
|
||||
}
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(scriptRevisionFileRecords))
|
||||
{
|
||||
InsertInput scriptRevisionFileInsertInput = new InsertInput();
|
||||
scriptRevisionFileInsertInput.setTableName(ScriptRevisionFile.TABLE_NAME);
|
||||
scriptRevisionFileInsertInput.setRecords(scriptRevisionFileRecords);
|
||||
scriptRevisionFileInsertInput.setTransaction(transaction);
|
||||
new InsertAction().execute(scriptRevisionFileInsertInput);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// update the script to point at the new revision //
|
||||
////////////////////////////////////////////////////
|
||||
script.setValue("currentScriptRevisionId", scriptRevision.getValue("id"));
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(script));
|
||||
updateInput.setTransaction(transaction);
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
transaction.commit();
|
||||
|
||||
output.addValue("scriptId", script.getValueInteger("id"));
|
||||
output.addValue("scriptName", script.getValueString("name"));
|
||||
output.addValue("scriptRevisionId", scriptRevisionId);
|
||||
output.addValue("scriptRevisionSequenceNo", scriptRevision.getValueInteger("sequenceNo"));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
scriptRevision.setValue("author", "Unknown");
|
||||
transaction.rollback();
|
||||
}
|
||||
finally
|
||||
{
|
||||
transaction.close();
|
||||
}
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName("scriptRevision");
|
||||
insertInput.setRecords(List.of(scriptRevision));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
scriptRevision = insertOutput.getRecords().get(0);
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// update the script to point at the new revision //
|
||||
////////////////////////////////////////////////////
|
||||
script.setValue("currentScriptRevisionId", scriptRevision.getValue("id"));
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(script));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
output.addValue("scriptId", script.getValueInteger("id"));
|
||||
output.addValue("scriptName", script.getValueString("name"));
|
||||
output.addValue("scriptRevisionId", scriptRevision.getValueInteger("id"));
|
||||
output.addValue("scriptRevisionSequenceNo", scriptRevision.getValueInteger("sequenceNo"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,35 +22,34 @@
|
||||
package com.kingsrook.qqq.backend.core.processes.implementations.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.RunAdHocRecordScriptAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
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.actions.scripts.RunAdHocRecordScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAdHocRecordScriptOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.TestScriptOutput;
|
||||
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.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AdHocScriptCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevision;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptRevisionFile;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -70,69 +69,84 @@ public class TestScriptProcessStep implements BackendStep
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
////////////////
|
||||
// get inputs //
|
||||
////////////////
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// build a script revision based on the input params & file contents //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
Integer scriptId = input.getValueInteger("scriptId");
|
||||
|
||||
ScriptRevision scriptRevision = new ScriptRevision();
|
||||
scriptRevision.setScriptId(scriptId);
|
||||
scriptRevision.setContents(input.getValueString("code"));
|
||||
|
||||
ArrayList<ScriptRevisionFile> files = new ArrayList<>();
|
||||
if(StringUtils.hasContent(input.getValueString("fileNames")))
|
||||
{
|
||||
for(String fileName : input.getValueString("fileNames").split(","))
|
||||
{
|
||||
files.add(new ScriptRevisionFile()
|
||||
.withFileName(fileName)
|
||||
.withContents(input.getValueString("fileContents:" + fileName)));
|
||||
}
|
||||
}
|
||||
|
||||
scriptRevision.setFiles(files);
|
||||
scriptRevision.setApiName(input.getValueString("apiName"));
|
||||
scriptRevision.setApiVersion(input.getValueString("apiVersion"));
|
||||
|
||||
BuildScriptLogAndScriptLogLineExecutionLogger executionLogger = new BuildScriptLogAndScriptLogLineExecutionLogger(null, null);
|
||||
///////////////////////////////////////////////////////
|
||||
// set up a code reference using the script revision //
|
||||
///////////////////////////////////////////////////////
|
||||
AdHocScriptCodeReference adHocScriptCodeReference = new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision.toQRecord());
|
||||
adHocScriptCodeReference.setCodeType(QCodeType.JAVA_SCRIPT); // todo - load dynamically?
|
||||
adHocScriptCodeReference.setInlineCode(scriptRevision.getFiles().get(0).getContents()); // todo - ugh.
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// lookup the script - figure out how to proceed based on type //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
QRecord script = getScript(scriptId);
|
||||
String scriptTypeName = getScriptTypeName(script);
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// load the script and its type, to find the TestScriptActionInterface where the script will be tested //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord script = getScript(scriptId);
|
||||
Integer scriptTypeId = script.getValueInteger("scriptTypeId");
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(ScriptType.TABLE_NAME);
|
||||
getInput.setPrimaryKey(scriptTypeId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
ScriptType scriptType = new ScriptType(getOutput.getRecord());
|
||||
|
||||
if(ScriptsMetaDataProvider.SCRIPT_TYPE_NAME_RECORD.equals(scriptTypeName))
|
||||
TestScriptActionInterface testScriptActionInterface = QCodeLoader.getAdHoc(TestScriptActionInterface.class, new QCodeReference(scriptType.getTestScriptInterfaceName(), QCodeType.JAVA));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// finish setting up input for the testScript action - including coyping over all input values //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
TestScriptInput testScriptInput = new TestScriptInput();
|
||||
testScriptInput.setApiName(input.getValueString("apiName"));
|
||||
testScriptInput.setApiVersion(input.getValueString("apiVersion"));
|
||||
testScriptInput.setCodeReference(adHocScriptCodeReference);
|
||||
|
||||
Map<String, Serializable> inputValues = new HashMap<>();
|
||||
testScriptInput.setInputValues(inputValues);
|
||||
|
||||
for(Map.Entry<String, Serializable> entry : input.getValues().entrySet())
|
||||
{
|
||||
String tableName = script.getValueString("tableName");
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
if(table == null)
|
||||
{
|
||||
throw (new QException("Could not find table [" + tableName + "] for script"));
|
||||
}
|
||||
|
||||
String recordPrimaryKeyList = input.getValueString("recordPrimaryKeyList");
|
||||
if(!StringUtils.hasContent(recordPrimaryKeyList))
|
||||
{
|
||||
throw (new QException("Record primary key list was not given."));
|
||||
}
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(tableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordPrimaryKeyList.split(","))));
|
||||
queryInput.setIncludeAssociations(true);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
if(CollectionUtils.nullSafeIsEmpty(queryOutput.getRecords()))
|
||||
{
|
||||
throw (new QException("No records were found by the given primary keys."));
|
||||
}
|
||||
|
||||
RunAdHocRecordScriptInput runAdHocRecordScriptInput = new RunAdHocRecordScriptInput();
|
||||
runAdHocRecordScriptInput.setRecordList(queryOutput.getRecords());
|
||||
runAdHocRecordScriptInput.setLogger(executionLogger);
|
||||
runAdHocRecordScriptInput.setTableName(tableName);
|
||||
runAdHocRecordScriptInput.setCodeReference(new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision.toQRecord()));
|
||||
RunAdHocRecordScriptOutput runAdHocRecordScriptOutput = new RunAdHocRecordScriptOutput();
|
||||
new RunAdHocRecordScriptAction().run(runAdHocRecordScriptInput, runAdHocRecordScriptOutput);
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// if there was an exception, send it back //
|
||||
/////////////////////////////////////////////
|
||||
runAdHocRecordScriptOutput.getException().ifPresent(e -> output.addValue("exception", e));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new QException("This process does not know how to test a script of type: " + scriptTypeName);
|
||||
String key = entry.getKey();
|
||||
String value = ValueUtils.getValueAsString(entry.getValue());
|
||||
inputValues.put(key, value);
|
||||
}
|
||||
|
||||
output.addValue("scriptLogLines", new ArrayList<>(executionLogger.getScriptLogLines()));
|
||||
////////////////////////////////
|
||||
// run the test script action //
|
||||
////////////////////////////////
|
||||
TestScriptOutput testScriptOutput = new TestScriptOutput();
|
||||
testScriptActionInterface.execute(testScriptInput, testScriptOutput);
|
||||
|
||||
//////////////////////////////////
|
||||
// send script outputs back out //
|
||||
//////////////////////////////////
|
||||
output.addValue("scriptLogLines", CollectionUtils.useOrWrap(testScriptOutput.getScriptLogLines(), TypeToken.get(ArrayList.class)));
|
||||
output.addValue("outputObject", testScriptOutput.getOutputObject());
|
||||
|
||||
if(testScriptOutput.getException() != null)
|
||||
{
|
||||
output.addValue("exception", testScriptOutput.getException());
|
||||
output.setException(testScriptOutput.getException());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -140,6 +154,7 @@ public class TestScriptProcessStep implements BackendStep
|
||||
// is this the kind of exception meant here? or is it more for one thrown by the script execution? or are those the same?? //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
output.addValue("exception", e);
|
||||
output.setException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,9 +38,13 @@ import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||
import com.kingsrook.qqq.backend.core.logging.LogPair;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.automation.QAutomationProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
@ -110,20 +114,9 @@ public class ScheduleManager
|
||||
*******************************************************************************/
|
||||
public void start()
|
||||
{
|
||||
String propertyName = "qqq.scheduleManager.enabled";
|
||||
String propertyValue = System.getProperty(propertyName);
|
||||
if("false".equals(propertyValue))
|
||||
if(!new QMetaDataVariableInterpreter().getBooleanFromPropertyOrEnvironment("qqq.scheduleManager.enabled", "QQQ_SCHEDULE_MANAGER_ENABLED", true))
|
||||
{
|
||||
LOG.info("Not starting ScheduleManager (per system property] [" + propertyName + "=" + propertyValue + "]).");
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaDataVariableInterpreter qMetaDataVariableInterpreter = new QMetaDataVariableInterpreter();
|
||||
String envName = "QQQ_SCHEDULE_MANAGER_ENABLED";
|
||||
String envValue = qMetaDataVariableInterpreter.interpret("${env." + envName + "}");
|
||||
if("false".equals(envValue))
|
||||
{
|
||||
LOG.info("Not starting ScheduleManager (per environment variable] [" + envName + "=" + envValue + "]).");
|
||||
LOG.info("Not starting ScheduleManager per settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -142,7 +135,7 @@ public class ScheduleManager
|
||||
if(process.getSchedule() != null && allowedToStart(process.getName()))
|
||||
{
|
||||
QScheduleMetaData scheduleMetaData = process.getSchedule();
|
||||
if(process.getSchedule().getBackendVariant() == null || QScheduleMetaData.RunStrategy.SERIAL.equals(process.getSchedule().getVariantRunStrategy()))
|
||||
if(process.getSchedule().getVariantBackend() == null || QScheduleMetaData.RunStrategy.SERIAL.equals(process.getSchedule().getVariantRunStrategy()))
|
||||
{
|
||||
///////////////////////////////////////////////
|
||||
// if no variants, or variant is serial mode //
|
||||
@ -156,11 +149,12 @@ public class ScheduleManager
|
||||
// running at the same time, get the variant records and schedule each separately //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QContext.init(qInstance, sessionSupplier.get());
|
||||
QBackendMetaData backendMetaData = qInstance.getBackend(scheduleMetaData.getVariantBackend());
|
||||
for(QRecord qRecord : CollectionUtils.nonNullList(getBackendVariantFilteredRecords(process)))
|
||||
{
|
||||
try
|
||||
{
|
||||
startProcess(process, MapBuilder.of(scheduleMetaData.getBackendVariant(), qRecord.getValue(scheduleMetaData.getVariantFieldName())));
|
||||
startProcess(process, MapBuilder.of(backendMetaData.getVariantOptionsTableTypeValue(), qRecord.getValue(backendMetaData.getVariantOptionsTableIdField())));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -170,7 +164,7 @@ public class ScheduleManager
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.error("Unsupported Schedule Run Strategy [" + process.getSchedule().getVariantFilter() + "] was provided.");
|
||||
LOG.error("Unsupported Schedule Run Strategy [" + process.getSchedule().getVariantRunStrategy() + "] was provided.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,10 +181,11 @@ public class ScheduleManager
|
||||
try
|
||||
{
|
||||
QScheduleMetaData scheduleMetaData = processMetaData.getSchedule();
|
||||
QBackendMetaData backendMetaData = qInstance.getBackend(scheduleMetaData.getVariantBackend());
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(scheduleMetaData.getVariantTableName());
|
||||
queryInput.setFilter(scheduleMetaData.getVariantFilter());
|
||||
queryInput.setTableName(backendMetaData.getVariantOptionsTableName());
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(backendMetaData.getVariantOptionsTableTypeField(), QCriteriaOperator.EQUALS, backendMetaData.getVariantOptionsTableTypeValue())));
|
||||
|
||||
QContext.init(qInstance, sessionSupplier.get());
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
@ -325,7 +320,7 @@ public class ScheduleManager
|
||||
|
||||
try
|
||||
{
|
||||
if(process.getSchedule().getBackendVariant() == null || QScheduleMetaData.RunStrategy.PARALLEL.equals(process.getSchedule().getVariantRunStrategy()))
|
||||
if(process.getSchedule().getVariantBackend() == null || QScheduleMetaData.RunStrategy.PARALLEL.equals(process.getSchedule().getVariantRunStrategy()))
|
||||
{
|
||||
QContext.init(qInstance, sessionSupplier.get());
|
||||
executeSingleProcess(process, backendVariantData);
|
||||
@ -342,7 +337,8 @@ public class ScheduleManager
|
||||
{
|
||||
QContext.init(qInstance, sessionSupplier.get());
|
||||
QScheduleMetaData scheduleMetaData = process.getSchedule();
|
||||
executeSingleProcess(process, MapBuilder.of(scheduleMetaData.getBackendVariant(), qRecord.getValue(scheduleMetaData.getVariantFieldName())));
|
||||
QBackendMetaData backendMetaData = qInstance.getBackend(scheduleMetaData.getVariantBackend());
|
||||
executeSingleProcess(process, MapBuilder.of(backendMetaData.getVariantOptionsTableTypeValue(), qRecord.getValue(backendMetaData.getVariantOptionsTableIdField())));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@ -34,6 +35,8 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
@ -550,4 +553,78 @@ public class CollectionUtils
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For cases where you have a Collection (of an unknown type), and you know you
|
||||
** want/need it in a specific concrete type (say, ArrayList), but you don't want
|
||||
** to just blindly copy it (e.g., as that may be expensive), call this method.
|
||||
**
|
||||
** ArrayList[String] myStrings = CollectionUtils.useOrWrap(yourStrings, new TypeToken<>() {});
|
||||
** CollectionUtils.useOrWrap(yourStrings, TypeToken.get(ArrayList.class));
|
||||
**
|
||||
** Note that you may always just pass `new TypeToken() {}` as the 2nd arg - then
|
||||
** the compiler will infer the type (T) based on the variable you're assigning into.
|
||||
*******************************************************************************/
|
||||
public static <E, T extends Collection<E>> T useOrWrap(Collection<E> collection, TypeToken<T> typeToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(collection == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
Class<T> targetClass = (Class<T>) typeToken.getRawType();
|
||||
if(targetClass.isInstance(collection))
|
||||
{
|
||||
return (targetClass.cast(collection));
|
||||
}
|
||||
|
||||
Constructor<T> constructor = targetClass.getConstructor(Collection.class);
|
||||
return (constructor.newInstance(collection));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QRuntimeException("Error wrapping collection", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For cases where you have a Collection (of an unknown type), and you know you
|
||||
** want/need it in a specific concrete type (say, ArrayList), but you don't want
|
||||
** to just blindly copy it (e.g., as that may be expensive), call this method.
|
||||
**
|
||||
** HashMap[String,Integer] myMap = CollectionUtils.useOrWrap(yourMap, new TypeToken<>() {});
|
||||
** CollectionUtils.useOrWrap(yourMap, TypeToken.get(HashMap.class));
|
||||
**
|
||||
** Note that you may always just pass `new TypeToken() {}` as the 2nd arg - then
|
||||
** the compiler will infer the type (T) based on the variable you're assigning into.
|
||||
*******************************************************************************/
|
||||
public static <K, V, T extends Map<K, V>> T useOrWrap(Map<K, V> collection, TypeToken<T> typeToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(collection == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
Class<T> targetClass = (Class<T>) typeToken.getRawType();
|
||||
if(targetClass.isInstance(collection))
|
||||
{
|
||||
return (targetClass.cast(collection));
|
||||
}
|
||||
|
||||
Constructor<T> constructor = targetClass.getConstructor(Map.class);
|
||||
return (constructor.newInstance(collection));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QRuntimeException("Error wrapping collection", e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user