mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merge branch 'release/0.6.0'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ target/
|
||||
*.iml
|
||||
.env
|
||||
|
||||
.idea
|
||||
|
||||
#############################################
|
||||
## Original contents from github template: ##
|
||||
|
3
pom.xml
3
pom.xml
@ -33,6 +33,7 @@
|
||||
<module>qqq-backend-module-api</module>
|
||||
<module>qqq-backend-module-filesystem</module>
|
||||
<module>qqq-backend-module-rdbms</module>
|
||||
<module>qqq-language-support-javascript</module>
|
||||
<module>qqq-middleware-picocli</module>
|
||||
<module>qqq-middleware-javalin</module>
|
||||
<module>qqq-middleware-lambda</module>
|
||||
@ -41,7 +42,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.6.0-SNAPSHOT</revision>
|
||||
<revision>0.6.0</revision>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
@ -51,6 +52,8 @@ public class QuickSightChartRenderer extends AbstractWidgetRenderer
|
||||
@Override
|
||||
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
try
|
||||
{
|
||||
QuickSightChartMetaData quickSightMetaData = (QuickSightChartMetaData) input.getWidgetMetaData();
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
@ -40,6 +41,8 @@ public class RenderWidgetAction
|
||||
*******************************************************************************/
|
||||
public RenderWidgetOutput execute(RenderWidgetInput input) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
AbstractWidgetRenderer widgetRenderer = QCodeLoader.getAdHoc(AbstractWidgetRenderer.class, input.getWidgetMetaData().getCodeReference());
|
||||
return (widgetRenderer.render(input));
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.interfaces;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for the Get action.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface GetInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
GetOutput execute(GetInput getInput) throws QException;
|
||||
}
|
@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
|
||||
@ -69,8 +70,10 @@ public class MetaDataAction
|
||||
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
|
||||
{
|
||||
tables.put(entry.getKey(), new QFrontendTableMetaData(entry.getValue(), false));
|
||||
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
|
||||
String tableName = entry.getKey();
|
||||
QBackendMetaData backendForTable = metaDataInput.getInstance().getBackendForTable(tableName);
|
||||
tables.put(tableName, new QFrontendTableMetaData(backendForTable, entry.getValue(), false));
|
||||
treeNodes.put(tableName, new AppTreeNode(entry.getValue()));
|
||||
}
|
||||
metaDataOutput.setTables(tables);
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.TableMetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
@ -52,7 +53,8 @@ public class TableMetaDataAction
|
||||
{
|
||||
throw (new QNotFoundException("Table [" + tableMetaDataInput.getTableName() + "] was not found."));
|
||||
}
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(table, true));
|
||||
QBackendMetaData backendForTable = tableMetaDataInput.getInstance().getBackendForTable(table.getName());
|
||||
tableMetaDataOutput.setTable(new QFrontendTableMetaData(backendForTable, table, true));
|
||||
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
|
@ -23,26 +23,43 @@ package com.kingsrook.qqq.backend.core.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
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.ProcessState;
|
||||
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.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
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.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
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.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
|
||||
import com.kingsrook.qqq.backend.core.state.StateProviderInterface;
|
||||
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -55,6 +72,15 @@ public class RunProcessAction
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(RunProcessAction.class);
|
||||
|
||||
public static final String BASEPULL_THIS_RUNTIME_KEY = "basepullThisRuntimeKey";
|
||||
public static final String BASEPULL_LAST_RUNTIME_KEY = "basepullLastRuntimeKey";
|
||||
public static final String BASEPULL_TIMESTAMP_FIELD = "basepullTimestampField";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// indicator that the timestamp field should be updated - e.g., the execute step is finished. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static final String BASEPULL_READY_TO_UPDATE_TIMESTAMP_FIELD = "basepullReadyToUpdateTimestamp";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -84,6 +110,18 @@ public class RunProcessAction
|
||||
UUIDAndTypeStateKey stateKey = new UUIDAndTypeStateKey(UUID.fromString(runProcessInput.getProcessUUID()), StateType.PROCESS_STATUS);
|
||||
ProcessState processState = primeProcessState(runProcessInput, stateKey, process);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// if process is 'basepull' style, keep track of 'now' //
|
||||
/////////////////////////////////////////////////////////
|
||||
BasepullConfiguration basepullConfiguration = process.getBasepullConfiguration();
|
||||
if(basepullConfiguration != null)
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// get the stored basepull timestamp //
|
||||
///////////////////////////////////////
|
||||
persistLastRunTime(runProcessInput, process, basepullConfiguration);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String lastStepName = runProcessInput.getStartAfterStep();
|
||||
@ -151,6 +189,15 @@ public class RunProcessAction
|
||||
throw (new QException("Unsure how to run a step of type: " + step.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// if 'basepull' style process, update the stored basepull timestamp //
|
||||
// but only when we've been signaled to do so - i.e., after an Execute step runs. //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
if(basepullConfiguration != null && BooleanUtils.isTrue(ValueUtils.getValueAsBoolean(runProcessInput.getValue(BASEPULL_READY_TO_UPDATE_TIMESTAMP_FIELD))))
|
||||
{
|
||||
storeLastRunTime(runProcessInput, process, basepullConfiguration);
|
||||
}
|
||||
}
|
||||
catch(QException qe)
|
||||
{
|
||||
@ -250,7 +297,17 @@ public class RunProcessAction
|
||||
runBackendStepInput.setTableName(process.getTableName());
|
||||
runBackendStepInput.setSession(runProcessInput.getSession());
|
||||
runBackendStepInput.setCallback(runProcessInput.getCallback());
|
||||
runBackendStepInput.setFrontendStepBehavior(runProcessInput.getFrontendStepBehavior());
|
||||
runBackendStepInput.setAsyncJobCallback(runProcessInput.getAsyncJobCallback());
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// if 'basepull' values are in the inputs, add to step input //
|
||||
///////////////////////////////////////////////////////////////
|
||||
if(runProcessInput.getValues().containsKey(BASEPULL_LAST_RUNTIME_KEY))
|
||||
{
|
||||
runBackendStepInput.setBasepullLastRunTime((Instant) runProcessInput.getValues().get(BASEPULL_LAST_RUNTIME_KEY));
|
||||
}
|
||||
|
||||
RunBackendStepOutput lastFunctionResult = new RunBackendStepAction().execute(runBackendStepInput);
|
||||
storeState(stateKey, lastFunctionResult.getProcessState());
|
||||
|
||||
@ -368,4 +425,129 @@ public class RunProcessAction
|
||||
return (getStateProvider().get(ProcessState.class, stateKey));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Insert or update the last runtime value for this basepull into the backend.
|
||||
*******************************************************************************/
|
||||
protected void storeLastRunTime(RunProcessInput runProcessInput, QProcessMetaData process, BasepullConfiguration basepullConfiguration) throws QException
|
||||
{
|
||||
String basepullTableName = basepullConfiguration.getTableName();
|
||||
String basepullKeyFieldName = basepullConfiguration.getKeyField();
|
||||
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
|
||||
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
|
||||
|
||||
///////////////////////////////////////
|
||||
// get the stored basepull timestamp //
|
||||
///////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput(runProcessInput.getInstance());
|
||||
queryInput.setSession(runProcessInput.getSession());
|
||||
queryInput.setTableName(basepullTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(
|
||||
new QFilterCriteria()
|
||||
.withFieldName(basepullKeyFieldName)
|
||||
.withOperator(QCriteriaOperator.EQUALS)
|
||||
.withValues(List.of(basepullKeyValue))));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
//////////////////////////////////////////
|
||||
// get the runtime for this process run //
|
||||
//////////////////////////////////////////
|
||||
Instant newRunTime = (Instant) runProcessInput.getValues().get(BASEPULL_THIS_RUNTIME_KEY);
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// update if found, otherwise insert new value //
|
||||
/////////////////////////////////////////////////
|
||||
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// update the basepull table with 'now' (which is before original query ran) //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QRecord basepullRecord = queryOutput.getRecords().get(0);
|
||||
basepullRecord.setValue(basepullLastRunTimeFieldName, newRunTime);
|
||||
|
||||
////////////
|
||||
// update //
|
||||
////////////
|
||||
UpdateInput updateInput = new UpdateInput(runProcessInput.getInstance());
|
||||
updateInput.setSession(runProcessInput.getSession());
|
||||
updateInput.setTableName(basepullTableName);
|
||||
updateInput.setRecords(List.of(basepullRecord));
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
QRecord basepullRecord = new QRecord()
|
||||
.withValue(basepullKeyFieldName, basepullKeyValue)
|
||||
.withValue(basepullLastRunTimeFieldName, newRunTime);
|
||||
|
||||
////////////////////////////////
|
||||
// insert new basepull record //
|
||||
////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput(runProcessInput.getInstance());
|
||||
insertInput.setSession(runProcessInput.getSession());
|
||||
insertInput.setTableName(basepullTableName);
|
||||
insertInput.setRecords(List.of(basepullRecord));
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Lookup the last runtime for this basepull, and set it (plus now) in the process's
|
||||
** values.
|
||||
*******************************************************************************/
|
||||
protected void persistLastRunTime(RunProcessInput runProcessInput, QProcessMetaData process, BasepullConfiguration basepullConfiguration) throws QException
|
||||
{
|
||||
////////////////////////////////////////////////////////
|
||||
// if these values were already computed, don't re-do //
|
||||
////////////////////////////////////////////////////////
|
||||
if(runProcessInput.getValue(BASEPULL_THIS_RUNTIME_KEY) != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// store 'now', which will be used to update basepull record if process completes successfully //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Instant now = Instant.now();
|
||||
runProcessInput.getValues().put(BASEPULL_THIS_RUNTIME_KEY, now);
|
||||
|
||||
String basepullTableName = basepullConfiguration.getTableName();
|
||||
String basepullKeyFieldName = basepullConfiguration.getKeyField();
|
||||
String basepullLastRunTimeFieldName = basepullConfiguration.getLastRunTimeFieldName();
|
||||
Integer basepullHoursBackForInitialTimestamp = basepullConfiguration.getHoursBackForInitialTimestamp();
|
||||
String basepullKeyValue = (basepullConfiguration.getKeyValue() != null) ? basepullConfiguration.getKeyValue() : process.getName();
|
||||
|
||||
///////////////////////////////////////
|
||||
// get the stored basepull timestamp //
|
||||
///////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput(runProcessInput.getInstance());
|
||||
queryInput.setSession(runProcessInput.getSession());
|
||||
queryInput.setTableName(basepullTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(
|
||||
new QFilterCriteria()
|
||||
.withFieldName(basepullKeyFieldName)
|
||||
.withOperator(QCriteriaOperator.EQUALS)
|
||||
.withValues(List.of(basepullKeyValue))));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the stored time, if not, default to 'now' unless a number of hours to offset was provided //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Instant lastRunTime = now;
|
||||
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
||||
{
|
||||
QRecord basepullRecord = queryOutput.getRecords().get(0);
|
||||
lastRunTime = ValueUtils.getValueAsInstant(basepullRecord.getValue(basepullLastRunTimeFieldName));
|
||||
}
|
||||
else if(basepullHoursBackForInitialTimestamp != null)
|
||||
{
|
||||
lastRunTime = lastRunTime.minus(basepullHoursBackForInitialTimestamp, ChronoUnit.HOURS);
|
||||
}
|
||||
|
||||
runProcessInput.getValues().put(BASEPULL_LAST_RUNTIME_KEY, lastRunTime);
|
||||
runProcessInput.getValues().put(BASEPULL_TIMESTAMP_FIELD, basepullConfiguration.getTimestampField());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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.exceptions.QCodeException;
|
||||
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.ExecuteCodeOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to execute user/runtime defined code.
|
||||
**
|
||||
** This action is designed to support code in multiple languages, by using
|
||||
** executors, e.g., provided by additional runtime qqq dependencies. Initially
|
||||
** we are building qqq-language-support-javascript.
|
||||
**
|
||||
** We also have a Java executor, to provide at least a little bit of testability
|
||||
** within qqq-backend-core. This executor is a candidate to be replaced in the
|
||||
** future with something that would do actual dynamic java (whether that's compiled
|
||||
** at runtime, or loaded from a plugin jar at runtime). In other words, the java
|
||||
** executor in place today is just meant to be a placeholder.
|
||||
*******************************************************************************/
|
||||
public class ExecuteCodeAction
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
public void run(ExecuteCodeInput input, ExecuteCodeOutput output) throws QException, QCodeException
|
||||
{
|
||||
QCodeReference codeReference = input.getCodeReference();
|
||||
|
||||
QCodeExecutionLoggerInterface executionLogger = input.getExecutionLogger();
|
||||
if(executionLogger == null)
|
||||
{
|
||||
executionLogger = getDefaultExecutionLogger();
|
||||
}
|
||||
executionLogger.acceptExecutionStart(input);
|
||||
|
||||
try
|
||||
{
|
||||
String languageExecutor = switch(codeReference.getCodeType())
|
||||
{
|
||||
case JAVA -> "com.kingsrook.qqq.backend.core.actions.scripts.QJavaExecutor";
|
||||
case JAVA_SCRIPT -> "com.kingsrook.qqq.languages.javascript.QJavaScriptExecutor";
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends QCodeExecutor> executorClass = (Class<? extends QCodeExecutor>) Class.forName(languageExecutor);
|
||||
QCodeExecutor qCodeExecutor = executorClass.getConstructor().newInstance();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// merge all of the input context, plus the input... input - into a context for the code executor //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Map<String, Serializable> context = new HashMap<>();
|
||||
if(input.getContext() != null)
|
||||
{
|
||||
context.putAll(input.getContext());
|
||||
}
|
||||
if(input.getInput() != null)
|
||||
{
|
||||
context.putAll(input.getInput());
|
||||
}
|
||||
|
||||
Serializable codeOutput = qCodeExecutor.execute(codeReference, context, executionLogger);
|
||||
output.setOutput(codeOutput);
|
||||
executionLogger.acceptExecutionEnd(codeOutput);
|
||||
}
|
||||
catch(QCodeException qCodeException)
|
||||
{
|
||||
executionLogger.acceptException(qCodeException);
|
||||
throw (qCodeException);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
executionLogger.acceptException(e);
|
||||
throw (new QException("Error executing code [" + codeReference + "]", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QCodeExecutionLoggerInterface getDefaultExecutionLogger()
|
||||
{
|
||||
return (new Log4jCodeExecutionLogger());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface to be implemented by language-specific code executors, e.g., in
|
||||
** qqq-language-support-${languageName} maven modules.
|
||||
*******************************************************************************/
|
||||
public interface QCodeExecutor
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
Serializable execute(QCodeReference codeReference, Map<String, Serializable> inputContext, QCodeExecutionLoggerInterface executionLogger) throws QCodeException;
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Java
|
||||
*******************************************************************************/
|
||||
public class QJavaExecutor implements QCodeExecutor
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable execute(QCodeReference codeReference, Map<String, Serializable> inputContext, QCodeExecutionLoggerInterface executionLogger) throws QCodeException
|
||||
{
|
||||
Map<String, Object> context = new HashMap<>(inputContext);
|
||||
if(!context.containsKey("logger"))
|
||||
{
|
||||
context.put("logger", executionLogger);
|
||||
}
|
||||
|
||||
Serializable output;
|
||||
try
|
||||
{
|
||||
Function<Map<String, Object>, Serializable> function = QCodeLoader.getFunction(codeReference);
|
||||
output = function.apply(context);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
QCodeException qCodeException = new QCodeException("Error executing script", e);
|
||||
throw (qCodeException);
|
||||
}
|
||||
|
||||
return (output);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
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.scripts.RunAssociatedScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptOutput;
|
||||
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.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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class RunAssociatedScriptAction
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void run(RunAssociatedScriptInput input, RunAssociatedScriptOutput output) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
Serializable scriptId = getScriptId(input);
|
||||
if(scriptId == null)
|
||||
{
|
||||
throw (new QNotFoundException("The input record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey()
|
||||
+ "] does not have a script specified for [" + input.getCodeReference().getFieldName() + "]"));
|
||||
}
|
||||
|
||||
Script script = getScript(input, scriptId);
|
||||
if(script.getCurrentScriptRevisionId() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey()
|
||||
+ "] (scriptId=" + scriptId + ") does not have a current version."));
|
||||
}
|
||||
|
||||
ScriptRevision scriptRevision = getCurrentScriptRevision(input, script.getCurrentScriptRevisionId());
|
||||
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(input.getInstance());
|
||||
executeCodeInput.setSession(input.getSession());
|
||||
executeCodeInput.setInput(new HashMap<>(input.getInputValues()));
|
||||
executeCodeInput.setContext(new HashMap<>());
|
||||
if(input.getOutputObject() != null)
|
||||
{
|
||||
executeCodeInput.getContext().put("output", input.getOutputObject());
|
||||
}
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(scriptRevision.getContents()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
|
||||
executeCodeInput.setExecutionLogger(new StoreScriptLogAndScriptLogLineExecutionLogger(scriptRevision.getScriptId(), scriptRevision.getId()));
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
|
||||
output.setOutput(executeCodeOutput.getOutput());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private ScriptRevision getCurrentScriptRevision(RunAssociatedScriptInput input, Serializable scriptRevisionId) throws QException
|
||||
{
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName("scriptRevision");
|
||||
getInput.setPrimaryKey(scriptRevisionId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The current revision of the script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "]["
|
||||
+ input.getCodeReference().getFieldName() + "] (scriptRevisionId=" + scriptRevisionId + ") was not found."));
|
||||
}
|
||||
|
||||
return (new ScriptRevision(getOutput.getRecord()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Script getScript(RunAssociatedScriptInput input, Serializable scriptId) throws QException
|
||||
{
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName("script");
|
||||
getInput.setPrimaryKey(scriptId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The script for record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "]["
|
||||
+ input.getCodeReference().getFieldName() + "] (script id=" + scriptId + ") was not found."));
|
||||
}
|
||||
|
||||
return (new Script(getOutput.getRecord()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Serializable getScriptId(RunAssociatedScriptInput input) throws QException
|
||||
{
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName(input.getCodeReference().getRecordTable());
|
||||
getInput.setPrimaryKey(input.getCodeReference().getRecordPrimaryKey());
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
throw (new QNotFoundException("The requested record [" + input.getCodeReference().getRecordTable() + "][" + input.getCodeReference().getRecordPrimaryKey() + "] was not found."));
|
||||
}
|
||||
|
||||
return (getOutput.getRecord().getValue(input.getCodeReference().getFieldName()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
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.scripts.StoreAssociatedScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.StoreAssociatedScriptOutput;
|
||||
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.QCriteriaOperator;
|
||||
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.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.metadata.tables.AssociatedScript;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to store a new version of a script, associated with a record.
|
||||
**
|
||||
** If there's never been a script assigned to the record (for the specified field),
|
||||
** then a new Script record is first inserted.
|
||||
**
|
||||
** The script referenced by the record is always updated to point at the new
|
||||
** scriptRevision record that is inserted.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class StoreAssociatedScriptAction
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void run(StoreAssociatedScriptInput input, StoreAssociatedScriptOutput output) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(input);
|
||||
|
||||
QTableMetaData table = input.getTable();
|
||||
Optional<AssociatedScript> optAssociatedScript = table.getAssociatedScripts().stream().filter(as -> as.getFieldName().equals(input.getFieldName())).findFirst();
|
||||
if(optAssociatedScript.isEmpty())
|
||||
{
|
||||
throw (new QException("Field to update associated script for is not an associated script field."));
|
||||
}
|
||||
AssociatedScript associatedScript = optAssociatedScript.get();
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// get the record that the script is to be associated with //
|
||||
/////////////////////////////////////////////////////////////
|
||||
QRecord associatedRecord;
|
||||
{
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName(input.getTableName());
|
||||
getInput.setPrimaryKey(input.getRecordPrimaryKey());
|
||||
getInput.setShouldGenerateDisplayValues(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
associatedRecord = getOutput.getRecord();
|
||||
}
|
||||
if(associatedRecord == null)
|
||||
{
|
||||
throw (new QException("Record to associated with script was not found."));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// check if there's currently a script referenced by the record //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
Serializable existingScriptId = associatedRecord.getValueString(input.getFieldName());
|
||||
QRecord script;
|
||||
Integer nextSequenceNo = 1;
|
||||
if(existingScriptId == null)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// get the script type - that'll be part of the new script's name //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName("scriptType");
|
||||
getInput.setPrimaryKey(associatedScript.getScriptTypeId());
|
||||
getInput.setShouldGenerateDisplayValues(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
QRecord scriptType = getOutput.getRecord();
|
||||
if(scriptType == null)
|
||||
{
|
||||
throw (new QException("Script type [" + associatedScript.getScriptTypeId() + "] was not found."));
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// insert a new script //
|
||||
/////////////////////////
|
||||
script = new QRecord();
|
||||
script.setValue("scriptTypeId", associatedScript.getScriptTypeId());
|
||||
script.setValue("name", associatedRecord.getRecordLabel() + " - " + scriptType.getRecordLabel());
|
||||
InsertInput insertInput = new InsertInput(input.getInstance());
|
||||
insertInput.setSession(input.getSession());
|
||||
insertInput.setTableName("script");
|
||||
insertInput.setRecords(List.of(script));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
script = insertOutput.getRecords().get(0);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// update the associated record to point at the new script //
|
||||
/////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput(input.getInstance());
|
||||
updateInput.setSession(input.getSession());
|
||||
updateInput.setTableName(input.getTableName());
|
||||
updateInput.setRecords(List.of(new QRecord()
|
||||
.withValue(table.getPrimaryKeyField(), associatedRecord.getValue(table.getPrimaryKeyField()))
|
||||
.withValue(input.getFieldName(), script.getValue("id"))
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////
|
||||
// get the existing script, to update //
|
||||
////////////////////////////////////////
|
||||
GetInput getInput = new GetInput(input.getInstance());
|
||||
getInput.setSession(input.getSession());
|
||||
getInput.setTableName("script");
|
||||
getInput.setPrimaryKey(existingScriptId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
script = getOutput.getRecord();
|
||||
|
||||
QueryInput queryInput = new QueryInput(input.getInstance());
|
||||
queryInput.setSession(input.getSession());
|
||||
queryInput.setTableName("scriptRevision");
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, List.of(script.getValue("id"))))
|
||||
.withOrderBy(new QFilterOrderBy("sequenceNo", false))
|
||||
);
|
||||
queryInput.setLimit(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.getCommitMessage();
|
||||
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.getCode())
|
||||
.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(input.getInstance());
|
||||
insertInput.setSession(input.getSession());
|
||||
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(input.getInstance());
|
||||
updateInput.setSession(input.getSession());
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(script));
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.BuildScriptLogAndScriptLogLineExecutionLogger;
|
||||
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.ExecuteCodeOutput;
|
||||
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.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Class for running a test of a script - e.g., maybe before it is saved.
|
||||
*******************************************************************************/
|
||||
public class TestScriptAction
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void run(TestScriptInput input, TestScriptOutput output) throws QException
|
||||
{
|
||||
QTableMetaData table = input.getTable();
|
||||
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(input.getInstance());
|
||||
executeCodeInput.setSession(input.getSession());
|
||||
executeCodeInput.setInput(new HashMap<>(input.getInputValues()));
|
||||
executeCodeInput.setContext(new HashMap<>());
|
||||
// todo! if(input.getOutputObject() != null)
|
||||
// todo! {
|
||||
// todo! executeCodeInput.getContext().put("output", input.getOutputObject());
|
||||
// todo! }
|
||||
executeCodeInput.setCodeReference(new QCodeReference().withInlineCode(input.getCode()).withCodeType(QCodeType.JAVA_SCRIPT)); // todo - code type as attribute of script!!
|
||||
executeCodeInput.setExecutionLogger(new BuildScriptLogAndScriptLogLineExecutionLogger());
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
|
||||
// todo! output.setOutput(executeCodeOutput.getOutput());
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that builds a scriptLog and 0 or more
|
||||
** scriptLogLine records - but doesn't insert them. e.g., useful for testing
|
||||
** (both in junit, and for users in-app).
|
||||
*******************************************************************************/
|
||||
public class BuildScriptLogAndScriptLogLineExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(BuildScriptLogAndScriptLogLineExecutionLogger.class);
|
||||
|
||||
private QRecord scriptLog;
|
||||
private List<QRecord> scriptLogLines = new ArrayList<>();
|
||||
protected ExecuteCodeInput executeCodeInput;
|
||||
|
||||
private Serializable scriptId;
|
||||
private Serializable scriptRevisionId;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BuildScriptLogAndScriptLogLineExecutionLogger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BuildScriptLogAndScriptLogLineExecutionLogger(Serializable scriptId, Serializable scriptRevisionId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QRecord buildHeaderRecord(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
return (new QRecord()
|
||||
.withValue("scriptId", scriptId)
|
||||
.withValue("scriptRevisionId", scriptRevisionId)
|
||||
.withValue("startTimestamp", Instant.now())
|
||||
.withValue("input", truncate(executeCodeInput.getInput())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected QRecord buildDetailLogRecord(String logLine)
|
||||
{
|
||||
return (new QRecord()
|
||||
.withValue("scriptLogId", scriptLog.getValue("id"))
|
||||
.withValue("timestamp", Instant.now())
|
||||
.withValue("text", truncate(logLine)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String truncate(Object o)
|
||||
{
|
||||
return StringUtils.safeTruncate(ValueUtils.getValueAsString(o), 1000, "...");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void updateHeaderAtEnd(Serializable output, Exception exception)
|
||||
{
|
||||
Instant startTimestamp = (Instant) scriptLog.getValue("startTimestamp");
|
||||
Instant endTimestamp = Instant.now();
|
||||
scriptLog.setValue("endTimestamp", endTimestamp);
|
||||
scriptLog.setValue("runTimeMillis", startTimestamp.until(endTimestamp, ChronoUnit.MILLIS));
|
||||
|
||||
if(exception != null)
|
||||
{
|
||||
scriptLog.setValue("hadError", true);
|
||||
scriptLog.setValue("error", exception.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptLog.setValue("hadError", false);
|
||||
scriptLog.setValue("output", truncate(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.executeCodeInput = executeCodeInput;
|
||||
this.scriptLog = buildHeaderRecord(executeCodeInput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error starting storage of script log", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
scriptLogLines.add(buildDetailLogRecord(logLine));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
updateHeaderAtEnd(null, exception);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
updateHeaderAtEnd(output, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptLog
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QRecord getScriptLog()
|
||||
{
|
||||
return scriptLog;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptLogLines
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QRecord> getScriptLogLines()
|
||||
{
|
||||
return scriptLogLines;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptLog
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void setScriptLog(QRecord scriptLog)
|
||||
{
|
||||
this.scriptLog = scriptLog;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that logs to LOG 4j
|
||||
*******************************************************************************/
|
||||
public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(Log4jCodeExecutionLogger.class);
|
||||
|
||||
private QCodeReference qCodeReference;
|
||||
private String uuid = UUID.randomUUID().toString();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
this.qCodeReference = executeCodeInput.getCodeReference();
|
||||
|
||||
String inputString = StringUtils.safeTruncate(ValueUtils.getValueAsString(executeCodeInput.getInput()), 250, "...");
|
||||
LOG.info("Starting script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with input: " + inputString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
LOG.info("Script log: " + uuid + ": " + logLine);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
LOG.info("Script Exception: " + uuid, exception);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
String outputString = StringUtils.safeTruncate(ValueUtils.getValueAsString(output), 250, "...");
|
||||
LOG.info("Finished script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with output: " + outputString);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that just noop's every action.
|
||||
*******************************************************************************/
|
||||
public class NoopCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface to provide logging functionality to QCodeExecution (e.g., scripts)
|
||||
*******************************************************************************/
|
||||
public interface QCodeExecutionLoggerInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Called when the execution starts - takes the execution's input object.
|
||||
*******************************************************************************/
|
||||
void acceptExecutionStart(ExecuteCodeInput executeCodeInput);
|
||||
|
||||
/*******************************************************************************
|
||||
** Called to log a line, a message.
|
||||
*******************************************************************************/
|
||||
void acceptLogLine(String logLine);
|
||||
|
||||
/*******************************************************************************
|
||||
** In case the loggerInterface object is provided to the script as context,
|
||||
** this method gives a clean interface for the script to log a line.
|
||||
*******************************************************************************/
|
||||
default void log(String message)
|
||||
{
|
||||
acceptLogLine(message);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Called if the script fails with an exception.
|
||||
*******************************************************************************/
|
||||
void acceptException(Exception exception);
|
||||
|
||||
/*******************************************************************************
|
||||
** Called if the script completes without exception.
|
||||
*******************************************************************************/
|
||||
void acceptExecutionEnd(Serializable output);
|
||||
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that logs into scriptLog and scriptLogLine
|
||||
** tables - e.g., as defined in ScriptMetaDataProvider.
|
||||
*******************************************************************************/
|
||||
public class StoreScriptLogAndScriptLogLineExecutionLogger extends BuildScriptLogAndScriptLogLineExecutionLogger
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(StoreScriptLogAndScriptLogLineExecutionLogger.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreScriptLogAndScriptLogLineExecutionLogger(Serializable scriptId, Serializable scriptRevisionId)
|
||||
{
|
||||
super(scriptId, scriptRevisionId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
super.acceptExecutionStart(executeCodeInput);
|
||||
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLog");
|
||||
insertInput.setRecords(List.of(getScriptLog()));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
setScriptLog(insertOutput.getRecords().get(0));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error starting storage of script log", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
store(null, exception);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
store(output, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void store(Serializable output, Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
updateHeaderAtEnd(output, exception);
|
||||
UpdateInput updateInput = new UpdateInput(executeCodeInput.getInstance());
|
||||
updateInput.setSession(executeCodeInput.getSession());
|
||||
updateInput.setTableName("scriptLog");
|
||||
updateInput.setRecords(List.of(getScriptLog()));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(getScriptLogLines()))
|
||||
{
|
||||
InsertInput insertInput = new InsertInput(executeCodeInput.getInstance());
|
||||
insertInput.setSession(executeCodeInput.getSession());
|
||||
insertInput.setTableName("scriptLogLine");
|
||||
insertInput.setRecords(getScriptLogLines());
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error storing script log", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
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.GetInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.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.modules.backend.QBackendModuleDispatcher;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Action to run a get against a table.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class GetAction
|
||||
{
|
||||
private Optional<Function<QRecord, QRecord>> postGetRecordCustomizer;
|
||||
|
||||
private GetInput getInput;
|
||||
private QValueFormatter qValueFormatter;
|
||||
private QPossibleValueTranslator qPossibleValueTranslator;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetOutput execute(GetInput getInput) throws QException
|
||||
{
|
||||
ActionHelper.validateSession(getInput);
|
||||
|
||||
postGetRecordCustomizer = QCodeLoader.getTableCustomizerFunction(getInput.getTable(), TableCustomizers.POST_QUERY_RECORD.getRole());
|
||||
this.getInput = getInput;
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(getInput.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
|
||||
GetInterface getInterface = null;
|
||||
try
|
||||
{
|
||||
getInterface = qModule.getGetInterface();
|
||||
}
|
||||
catch(IllegalStateException ise)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if a module doesn't implement Get directly - try to do a Get by a Query by the primary key //
|
||||
// see below. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
GetOutput getOutput;
|
||||
if(getInterface != null)
|
||||
{
|
||||
getOutput = getInterface.execute(getInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
getOutput = performGetViaQuery(getInput);
|
||||
}
|
||||
|
||||
if(getOutput.getRecord() != null)
|
||||
{
|
||||
getOutput.setRecord(postRecordActions(getOutput.getRecord()));
|
||||
}
|
||||
|
||||
return getOutput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private GetOutput performGetViaQuery(GetInput getInput) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(getInput.getInstance());
|
||||
queryInput.setSession(getInput.getSession());
|
||||
queryInput.setTableName(getInput.getTableName());
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(getInput.getTable().getPrimaryKeyField(), QCriteriaOperator.EQUALS, List.of(getInput.getPrimaryKey()))));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
GetOutput getOutput = new GetOutput();
|
||||
if(!queryOutput.getRecords().isEmpty())
|
||||
{
|
||||
getOutput.setRecord(queryOutput.getRecords().get(0));
|
||||
}
|
||||
return (getOutput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Run the necessary actions on a record. This may include setting display values,
|
||||
** translating possible values, and running post-record customizations.
|
||||
*******************************************************************************/
|
||||
public QRecord postRecordActions(QRecord record)
|
||||
{
|
||||
QRecord returnRecord = record;
|
||||
if(this.postGetRecordCustomizer.isPresent())
|
||||
{
|
||||
returnRecord = postGetRecordCustomizer.get().apply(record);
|
||||
}
|
||||
|
||||
if(getInput.getShouldTranslatePossibleValues())
|
||||
{
|
||||
if(qPossibleValueTranslator == null)
|
||||
{
|
||||
qPossibleValueTranslator = new QPossibleValueTranslator(getInput.getInstance(), getInput.getSession());
|
||||
}
|
||||
qPossibleValueTranslator.translatePossibleValuesInRecords(getInput.getTable(), List.of(returnRecord));
|
||||
}
|
||||
|
||||
if(getInput.getShouldGenerateDisplayValues())
|
||||
{
|
||||
if(qValueFormatter == null)
|
||||
{
|
||||
qValueFormatter = new QValueFormatter();
|
||||
}
|
||||
qValueFormatter.setDisplayValuesInRecords(getInput.getTable(), List.of(returnRecord));
|
||||
}
|
||||
|
||||
return (returnRecord);
|
||||
}
|
||||
}
|
@ -406,8 +406,11 @@ public class QPossibleValueTranslator
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this is needed to get record labels, which are what we use here... unclear if best! //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
if(notTooDeep())
|
||||
{
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
}
|
||||
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
@ -428,4 +431,24 @@ public class QPossibleValueTranslator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Avoid infinite recursion, for where one field's PVS depends on another's...
|
||||
** not too smart, just breaks at 5...
|
||||
*******************************************************************************/
|
||||
private boolean notTooDeep()
|
||||
{
|
||||
int count = 0;
|
||||
for(StackTraceElement stackTraceElement : new Throwable().getStackTrace())
|
||||
{
|
||||
if(stackTraceElement.getMethodName().equals("translatePossibleValuesInRecords"))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (count < 5);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ public class QValueFormatter
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Error formatting record label", e);
|
||||
return (formatRecordLabelExceptionalCases(table, record));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.exceptions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Exception thrown while executing custom code in QQQ.
|
||||
**
|
||||
** Context field is meant to give the user "context" for where the error occurred
|
||||
** - e.g., a line number or word that was bad.
|
||||
*******************************************************************************/
|
||||
public class QCodeException extends QException
|
||||
{
|
||||
private String context;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor of message
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor of message & cause
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getContext()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setContext(String context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
}
|
@ -316,6 +316,24 @@ public class QInstanceEnricher
|
||||
{
|
||||
generateAppSections(app);
|
||||
}
|
||||
|
||||
for(QAppSection section : CollectionUtils.nonNullList(app.getSections()))
|
||||
{
|
||||
enrichAppSection(section);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrichAppSection(QAppSection section)
|
||||
{
|
||||
if(!StringUtils.hasContent(section.getLabel()))
|
||||
{
|
||||
section.setLabel(nameToLabel(section.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -590,7 +608,7 @@ public class QInstanceEnricher
|
||||
** <li>TLAAndAnotherTLA -> tla_and_another_tla</li>
|
||||
** </ul>
|
||||
*******************************************************************************/
|
||||
static String inferBackendName(String fieldName)
|
||||
public static String inferBackendName(String fieldName)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// build a list of words in the name, then join them with _ and lower-case the result //
|
||||
|
@ -31,13 +31,16 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
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.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||
@ -50,6 +53,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
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;
|
||||
@ -118,6 +123,7 @@ public class QInstanceValidator
|
||||
validateAutomationProviders(qInstance);
|
||||
validateTables(qInstance);
|
||||
validateProcesses(qInstance);
|
||||
validateReports(qInstance);
|
||||
validateApps(qInstance);
|
||||
validatePossibleValueSources(qInstance);
|
||||
validateQueuesAndProviders(qInstance);
|
||||
@ -186,6 +192,8 @@ public class QInstanceValidator
|
||||
qInstance.getBackends().forEach((backendName, backend) ->
|
||||
{
|
||||
assertCondition(Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + ".");
|
||||
|
||||
backend.performValidation(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -258,6 +266,8 @@ public class QInstanceValidator
|
||||
//////////////////////////////////////////
|
||||
Set<String> fieldNamesInSections = new HashSet<>();
|
||||
QFieldSection tier1Section = null;
|
||||
Set<String> usedSectionNames = new HashSet<>();
|
||||
Set<String> usedSectionLabels = new HashSet<>();
|
||||
if(table.getSections() != null)
|
||||
{
|
||||
for(QFieldSection section : table.getSections())
|
||||
@ -268,6 +278,12 @@ public class QInstanceValidator
|
||||
assertCondition(tier1Section == null, "Table " + tableName + " has more than 1 section listed as Tier 1");
|
||||
tier1Section = section;
|
||||
}
|
||||
|
||||
assertCondition(!usedSectionNames.contains(section.getName()), "Table " + tableName + " has more than 1 section named " + section.getName());
|
||||
usedSectionNames.add(section.getName());
|
||||
|
||||
assertCondition(!usedSectionLabels.contains(section.getLabel()), "Table " + tableName + " has more than 1 section labeled " + section.getLabel());
|
||||
usedSectionLabels.add(section.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,6 +732,133 @@ public class QInstanceValidator
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateReports(QInstance qInstance)
|
||||
{
|
||||
if(CollectionUtils.nullSafeHasContents(qInstance.getReports()))
|
||||
{
|
||||
qInstance.getReports().forEach((reportName, report) ->
|
||||
{
|
||||
assertCondition(Objects.equals(reportName, report.getName()), "Inconsistent naming for report: " + reportName + "/" + report.getName() + ".");
|
||||
validateAppChildHasValidParentAppName(qInstance, report);
|
||||
|
||||
////////////////////////////////////////
|
||||
// validate dataSources in the report //
|
||||
////////////////////////////////////////
|
||||
Set<String> usedDataSourceNames = new HashSet<>();
|
||||
if(assertCondition(CollectionUtils.nullSafeHasContents(report.getDataSources()), "At least 1 data source must be defined in report " + reportName + "."))
|
||||
{
|
||||
int index = 0;
|
||||
for(QReportDataSource dataSource : report.getDataSources())
|
||||
{
|
||||
assertCondition(StringUtils.hasContent(dataSource.getName()), "Missing name for a dataSource at index " + index + " in report " + reportName);
|
||||
index++;
|
||||
|
||||
assertCondition(!usedDataSourceNames.contains(dataSource.getName()), "More than one dataSource with name " + dataSource.getName() + " in report " + reportName);
|
||||
usedDataSourceNames.add(dataSource.getName());
|
||||
|
||||
String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " ";
|
||||
|
||||
if(StringUtils.hasContent(dataSource.getSourceTable()))
|
||||
{
|
||||
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (exactly 1 is required).");
|
||||
if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance."))
|
||||
{
|
||||
if(dataSource.getQueryFilter() != null)
|
||||
{
|
||||
validateQueryFilter("In " + dataSourceErrorPrefix + "query filter - ", qInstance.getTable(dataSource.getSourceTable()), dataSource.getQueryFilter());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dataSource.getStaticDataSupplier() != null)
|
||||
{
|
||||
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getStaticDataSupplier(), Supplier.class);
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.add(dataSourceErrorPrefix + "does not have a sourceTable or a staticDataSupplier (exactly 1 is required).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// validate dataSources in the report //
|
||||
////////////////////////////////////////
|
||||
if(assertCondition(CollectionUtils.nullSafeHasContents(report.getViews()), "At least 1 view must be defined in report " + reportName + "."))
|
||||
{
|
||||
int index = 0;
|
||||
Set<String> usedViewNames = new HashSet<>();
|
||||
for(QReportView view : report.getViews())
|
||||
{
|
||||
assertCondition(StringUtils.hasContent(view.getName()), "Missing name for a view at index " + index + " in report " + reportName);
|
||||
index++;
|
||||
|
||||
assertCondition(!usedViewNames.contains(view.getName()), "More than one view with name " + view.getName() + " in report " + reportName);
|
||||
usedViewNames.add(view.getName());
|
||||
|
||||
String viewErrorPrefix = "Report " + reportName + " view " + view.getName() + " ";
|
||||
assertCondition(view.getType() != null, viewErrorPrefix + " is missing its type.");
|
||||
if(assertCondition(StringUtils.hasContent(view.getDataSourceName()), viewErrorPrefix + " is missing a dataSourceName"))
|
||||
{
|
||||
assertCondition(usedDataSourceNames.contains(view.getDataSourceName()), viewErrorPrefix + " has an unrecognized dataSourceName: " + view.getDataSourceName());
|
||||
}
|
||||
|
||||
if(StringUtils.hasContent(view.getVarianceDataSourceName()))
|
||||
{
|
||||
assertCondition(usedDataSourceNames.contains(view.getVarianceDataSourceName()), viewErrorPrefix + " has an unrecognized varianceDataSourceName: " + view.getVarianceDataSourceName());
|
||||
}
|
||||
|
||||
// actually, this is okay if there's a customizer, so...
|
||||
assertCondition(CollectionUtils.nullSafeHasContents(view.getColumns()), viewErrorPrefix + " does not have any columns.");
|
||||
|
||||
// todo - all these too...
|
||||
// view.getPivotFields();
|
||||
// view.getViewCustomizer(); // validate code ref
|
||||
// view.getRecordTransformStep(); // validate code ref
|
||||
// view.getOrderByFields(); // make sure valid field names?
|
||||
// view.getIncludePivotSubTotals(); // only for pivot type
|
||||
// view.getTitleFormat(); view.getTitleFields(); // validate these match?
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void validateQueryFilter(String context, QTableMetaData table, QQueryFilter queryFilter)
|
||||
{
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||
{
|
||||
if(assertCondition(StringUtils.hasContent(criterion.getFieldName()), context + "Missing fieldName for a criteria"))
|
||||
{
|
||||
assertNoException(() -> table.getField(criterion.getFieldName()), context + "Criteria fieldName " + criterion.getFieldName() + " is not a field in this table.");
|
||||
}
|
||||
assertCondition(criterion.getOperator() != null, context + "Missing operator for a criteria on fieldName " + criterion.getFieldName());
|
||||
assertCondition(criterion.getValues() != null, context + "Missing values for a criteria on fieldName " + criterion.getFieldName()); // todo - what about ops w/ no value (BLANK)
|
||||
}
|
||||
|
||||
for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys()))
|
||||
{
|
||||
if(assertCondition(StringUtils.hasContent(orderBy.getFieldName()), context + "Missing fieldName for an orderBy"))
|
||||
{
|
||||
assertNoException(() -> table.getField(orderBy.getFieldName()), context + "OrderBy fieldName " + orderBy.getFieldName() + " is not a field in this table.");
|
||||
}
|
||||
}
|
||||
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
|
||||
{
|
||||
validateQueryFilter(context, table, subFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -978,7 +1121,7 @@ public class QInstanceValidator
|
||||
** But if it's false, add the provided message to the list of errors (and return false,
|
||||
** e.g., in case you need to stop evaluating rules to avoid exceptions).
|
||||
*******************************************************************************/
|
||||
private boolean assertCondition(boolean condition, String message)
|
||||
public boolean assertCondition(boolean condition, String message)
|
||||
{
|
||||
if(!condition)
|
||||
{
|
||||
@ -1035,4 +1178,15 @@ public class QInstanceValidator
|
||||
LOG.info("Validation warning: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for errors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<String> getErrors()
|
||||
{
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -44,12 +45,14 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
*******************************************************************************/
|
||||
public class RunBackendStepInput extends AbstractActionInput
|
||||
{
|
||||
private ProcessState processState;
|
||||
private String processName;
|
||||
private String tableName;
|
||||
private String stepName;
|
||||
private QProcessCallback callback;
|
||||
private AsyncJobCallback asyncJobCallback;
|
||||
private ProcessState processState;
|
||||
private String processName;
|
||||
private String tableName;
|
||||
private String stepName;
|
||||
private QProcessCallback callback;
|
||||
private AsyncJobCallback asyncJobCallback;
|
||||
private RunProcessInput.FrontendStepBehavior frontendStepBehavior;
|
||||
private Instant basepullLastRunTime;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// note - new fields should generally be added in method: cloneFieldsInto //
|
||||
@ -416,6 +419,17 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for a single field's value
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getValueInstant(String fieldName)
|
||||
{
|
||||
return (ValueUtils.getValueAsInstant(getValue(fieldName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Accessor for processState - protected, because we generally want to access
|
||||
** its members through wrapper methods, we think
|
||||
@ -453,4 +467,72 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
return (asyncJobCallback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for frontendStepBehavior
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunProcessInput.FrontendStepBehavior getFrontendStepBehavior()
|
||||
{
|
||||
return frontendStepBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for frontendStepBehavior
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior frontendStepBehavior)
|
||||
{
|
||||
this.frontendStepBehavior = frontendStepBehavior;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for frontendStepBehavior
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunBackendStepInput withFrontendStepBehavior(RunProcessInput.FrontendStepBehavior frontendStepBehavior)
|
||||
{
|
||||
this.frontendStepBehavior = frontendStepBehavior;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for basepullLastRunTime
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getBasepullLastRunTime()
|
||||
{
|
||||
return basepullLastRunTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for basepullLastRunTime
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBasepullLastRunTime(Instant basepullLastRunTime)
|
||||
{
|
||||
this.basepullLastRunTime = basepullLastRunTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for basepullLastRunTime
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunBackendStepInput withBasepullLastRunTime(Instant basepullLastRunTime)
|
||||
{
|
||||
this.basepullLastRunTime = basepullLastRunTime;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ExecuteCodeInput extends AbstractActionInput
|
||||
{
|
||||
private QCodeReference codeReference;
|
||||
private Map<String, Serializable> input;
|
||||
private Map<String, Serializable> context;
|
||||
private QCodeExecutionLoggerInterface executionLogger;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput(QInstance qInstance)
|
||||
{
|
||||
super(qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeReference getCodeReference()
|
||||
{
|
||||
return codeReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCodeReference(QCodeReference codeReference)
|
||||
{
|
||||
this.codeReference = codeReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withCodeReference(QCodeReference codeReference)
|
||||
{
|
||||
this.codeReference = codeReference;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getInput()
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInput(Map<String, Serializable> input)
|
||||
{
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withInput(Map<String, Serializable> input)
|
||||
{
|
||||
this.input = input;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withInput(String key, Serializable value)
|
||||
{
|
||||
if(this.input == null)
|
||||
{
|
||||
input = new HashMap<>();
|
||||
}
|
||||
this.input.put(key, value);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getContext()
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setContext(Map<String, Serializable> context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withContext(Map<String, Serializable> context)
|
||||
{
|
||||
this.context = context;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for context
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withContext(String key, Serializable value)
|
||||
{
|
||||
if(this.context == null)
|
||||
{
|
||||
context = new HashMap<>();
|
||||
}
|
||||
this.context.put(key, value);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for executionLogger
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeExecutionLoggerInterface getExecutionLogger()
|
||||
{
|
||||
return executionLogger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for executionLogger
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setExecutionLogger(QCodeExecutionLoggerInterface executionLogger)
|
||||
{
|
||||
this.executionLogger = executionLogger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for executionLogger
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeInput withExecutionLogger(QCodeExecutionLoggerInterface executionLogger)
|
||||
{
|
||||
this.executionLogger = executionLogger;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ExecuteCodeOutput
|
||||
{
|
||||
private Serializable output;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getOutput()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOutput(Serializable output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ExecuteCodeOutput withOutput(Serializable output)
|
||||
{
|
||||
this.output = output;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class RunAssociatedScriptInput extends AbstractTableActionInput
|
||||
{
|
||||
private AssociatedScriptCodeReference codeReference;
|
||||
private Map<String, Serializable> inputValues;
|
||||
|
||||
private Serializable outputObject;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput(QInstance qInstance)
|
||||
{
|
||||
super(qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScriptCodeReference getCodeReference()
|
||||
{
|
||||
return codeReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCodeReference(AssociatedScriptCodeReference codeReference)
|
||||
{
|
||||
this.codeReference = codeReference;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for codeReference
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput withCodeReference(AssociatedScriptCodeReference codeReference)
|
||||
{
|
||||
this.codeReference = codeReference;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getInputValues()
|
||||
{
|
||||
return inputValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInputValues(Map<String, Serializable> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput withInputValues(Map<String, Serializable> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getOutputObject()
|
||||
{
|
||||
return outputObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOutputObject(Serializable outputObject)
|
||||
{
|
||||
this.outputObject = outputObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for outputObject
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptInput withOutputObject(Serializable outputObject)
|
||||
{
|
||||
this.outputObject = outputObject;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class RunAssociatedScriptOutput extends AbstractActionOutput
|
||||
{
|
||||
private Serializable output;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getOutput()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOutput(Serializable output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RunAssociatedScriptOutput withOutput(Serializable output)
|
||||
{
|
||||
this.output = output;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class StoreAssociatedScriptInput extends AbstractTableActionInput
|
||||
{
|
||||
private String fieldName;
|
||||
private Serializable recordPrimaryKey;
|
||||
|
||||
private String code;
|
||||
private String commitMessage;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreAssociatedScriptInput(QInstance instance)
|
||||
{
|
||||
super(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreAssociatedScriptInput withFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getRecordPrimaryKey()
|
||||
{
|
||||
return recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreAssociatedScriptInput withRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreAssociatedScriptInput withCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getCommitMessage()
|
||||
{
|
||||
return commitMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCommitMessage(String commitMessage)
|
||||
{
|
||||
this.commitMessage = commitMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StoreAssociatedScriptInput withCommitMessage(String commitMessage)
|
||||
{
|
||||
this.commitMessage = commitMessage;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class StoreAssociatedScriptOutput extends AbstractActionOutput
|
||||
{
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestScriptInput extends AbstractTableActionInput
|
||||
{
|
||||
private Serializable recordPrimaryKey;
|
||||
private String code;
|
||||
private Serializable scriptTypeId;
|
||||
private Map<String, String> inputValues;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TestScriptInput(QInstance qInstance)
|
||||
{
|
||||
super(qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getRecordPrimaryKey()
|
||||
{
|
||||
return recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TestScriptInput withRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getInputValues()
|
||||
{
|
||||
return inputValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInputValues(Map<String, String> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for inputValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TestScriptInput withInputValues(Map<String, String> inputValues)
|
||||
{
|
||||
this.inputValues = inputValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for code
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TestScriptInput withCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getScriptTypeId()
|
||||
{
|
||||
return scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptTypeId(Serializable scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TestScriptInput withScriptTypeId(Serializable scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.actions.scripts;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestScriptOutput extends AbstractActionOutput
|
||||
{
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.actions.tables.get;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Input data for the Get action
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class GetInput extends AbstractTableActionInput
|
||||
{
|
||||
private QBackendTransaction transaction;
|
||||
private Serializable primaryKey;
|
||||
|
||||
private boolean shouldTranslatePossibleValues = false;
|
||||
private boolean shouldGenerateDisplayValues = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput(QInstance instance)
|
||||
{
|
||||
super(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput(QInstance instance, QSession session)
|
||||
{
|
||||
super(instance);
|
||||
setSession(session);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for primaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getPrimaryKey()
|
||||
{
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for primaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPrimaryKey(Serializable primaryKey)
|
||||
{
|
||||
this.primaryKey = primaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for primaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput withPrimaryKey(Serializable primaryKey)
|
||||
{
|
||||
this.primaryKey = primaryKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shouldTranslatePossibleValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getShouldTranslatePossibleValues()
|
||||
{
|
||||
return shouldTranslatePossibleValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shouldTranslatePossibleValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setShouldTranslatePossibleValues(boolean shouldTranslatePossibleValues)
|
||||
{
|
||||
this.shouldTranslatePossibleValues = shouldTranslatePossibleValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for shouldGenerateDisplayValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getShouldGenerateDisplayValues()
|
||||
{
|
||||
return shouldGenerateDisplayValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for shouldGenerateDisplayValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setShouldGenerateDisplayValues(boolean shouldGenerateDisplayValues)
|
||||
{
|
||||
this.shouldGenerateDisplayValues = shouldGenerateDisplayValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for transaction
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendTransaction getTransaction()
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for transaction
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTransaction(QBackendTransaction transaction)
|
||||
{
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for transaction
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetInput withTransaction(QBackendTransaction transaction)
|
||||
{
|
||||
this.transaction = transaction;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.actions.tables.get;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Output for a Get action
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class GetOutput extends AbstractActionOutput implements Serializable
|
||||
{
|
||||
private QRecord record;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for record
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QRecord getRecord()
|
||||
{
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for record
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRecord(QRecord record)
|
||||
{
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for record
|
||||
**
|
||||
*******************************************************************************/
|
||||
public GetOutput withRecord(QRecord record)
|
||||
{
|
||||
this.record = record;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -25,6 +25,9 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -33,6 +36,8 @@ import java.util.List;
|
||||
*******************************************************************************/
|
||||
public class QFilterCriteria implements Serializable, Cloneable
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(QFilterCriteria.class);
|
||||
|
||||
private String fieldName;
|
||||
private QCriteriaOperator operator;
|
||||
private List<Serializable> values;
|
||||
@ -183,4 +188,46 @@ public class QFilterCriteria implements Serializable, Cloneable
|
||||
this.values = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder rs = new StringBuilder(fieldName);
|
||||
try
|
||||
{
|
||||
rs.append(" ").append(operator).append(" ");
|
||||
if(CollectionUtils.nullSafeHasContents(values))
|
||||
{
|
||||
if(values.size() == 1)
|
||||
{
|
||||
rs.append(values.get(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = 0;
|
||||
for(Serializable value : values)
|
||||
{
|
||||
if(index++ > 9)
|
||||
{
|
||||
rs.append("and ").append(values.size() - index).append(" more");
|
||||
break;
|
||||
}
|
||||
rs.append(value).append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error in toString", e);
|
||||
rs.append("Error generating toString...");
|
||||
}
|
||||
|
||||
return (rs.toString());
|
||||
}
|
||||
}
|
||||
|
@ -151,4 +151,14 @@ public class QFilterOrderBy implements Serializable, Cloneable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return (fieldName + " " + (isAscending ? "ASC" : "DESC"));
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -34,6 +36,8 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
*******************************************************************************/
|
||||
public class QQueryFilter implements Serializable, Cloneable
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(QQueryFilter.class);
|
||||
|
||||
private List<QFilterCriteria> criteria = new ArrayList<>();
|
||||
private List<QFilterOrderBy> orderBys = new ArrayList<>();
|
||||
|
||||
@ -301,4 +305,41 @@ public class QQueryFilter implements Serializable, Cloneable
|
||||
subFilters.add(subFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder rs = new StringBuilder("(");
|
||||
try
|
||||
{
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(criteria))
|
||||
{
|
||||
rs.append(criterion).append(" ").append(getBooleanOperator());
|
||||
}
|
||||
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(subFilters))
|
||||
{
|
||||
rs.append(subFilter);
|
||||
}
|
||||
rs.append(")");
|
||||
|
||||
rs.append("OrderBy[");
|
||||
for(QFilterOrderBy orderBy : orderBys)
|
||||
{
|
||||
rs.append(orderBy).append(",");
|
||||
}
|
||||
rs.append("]");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error in toString", e);
|
||||
rs.append("Error generating toString...");
|
||||
}
|
||||
|
||||
return (rs.toString());
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions.tables.update;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
@ -54,4 +55,18 @@ public class UpdateOutput extends AbstractActionOutput
|
||||
{
|
||||
this.records = records;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addRecord(QRecord record)
|
||||
{
|
||||
if(this.records == null)
|
||||
{
|
||||
this.records = new ArrayList<>();
|
||||
}
|
||||
this.records.add(record);
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,8 @@ public class QRecord implements Serializable
|
||||
private Map<String, Serializable> backendDetails = new LinkedHashMap<>();
|
||||
private List<String> errors = new ArrayList<>();
|
||||
|
||||
public static final String BACKEND_DETAILS_TYPE_JSON_SOURCE_OBJECT = "jsonSourceObject";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -455,6 +457,11 @@ public class QRecord implements Serializable
|
||||
*******************************************************************************/
|
||||
public Serializable getBackendDetail(String key)
|
||||
{
|
||||
if(!this.backendDetails.containsKey(key))
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return this.backendDetails.get(key);
|
||||
}
|
||||
|
||||
@ -466,7 +473,7 @@ public class QRecord implements Serializable
|
||||
*******************************************************************************/
|
||||
public String getBackendDetailString(String key)
|
||||
{
|
||||
return (String) this.backendDetails.get(key);
|
||||
return (String) getBackendDetail(key);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Base class for enums that are interoperable with QRecords.
|
||||
*******************************************************************************/
|
||||
public interface QRecordEnum
|
||||
{
|
||||
ListingHash<Class<? extends QRecordEnum>, QRecordEntityField> fieldMapping = new ListingHash<>();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Convert this entity to a QRecord.
|
||||
**
|
||||
*******************************************************************************/
|
||||
default QRecord toQRecord() throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
QRecord qRecord = new QRecord();
|
||||
|
||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
||||
{
|
||||
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
||||
}
|
||||
|
||||
return (qRecord);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error building qRecord from entity.", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static List<QRecordEntityField> getFieldList(Class<? extends QRecordEnum> c)
|
||||
{
|
||||
if(!fieldMapping.containsKey(c))
|
||||
{
|
||||
List<QRecordEntityField> fieldList = new ArrayList<>();
|
||||
for(Method possibleGetter : c.getMethods())
|
||||
{
|
||||
if(isGetter(possibleGetter))
|
||||
{
|
||||
String fieldName = getFieldNameFromGetter(possibleGetter);
|
||||
Optional<QField> fieldAnnotation = getQFieldAnnotation(c, fieldName);
|
||||
fieldList.add(new QRecordEntityField(fieldName, possibleGetter, null, possibleGetter.getReturnType(), fieldAnnotation.orElse(null)));
|
||||
}
|
||||
}
|
||||
fieldMapping.put(c, fieldList);
|
||||
}
|
||||
return (fieldMapping.get(c));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Optional<QField> getQFieldAnnotation(Class<? extends QRecordEnum> c, String fieldName)
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = c.getDeclaredField(fieldName);
|
||||
return (Optional.ofNullable(field.getAnnotation(QField.class)));
|
||||
}
|
||||
catch(NoSuchFieldException e)
|
||||
{
|
||||
//////////////////////////////////////////
|
||||
// ok, we just won't have an annotation //
|
||||
//////////////////////////////////////////
|
||||
}
|
||||
return (Optional.empty());
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String getFieldNameFromGetter(Method getter)
|
||||
{
|
||||
String nameWithoutGet = getter.getName().replaceFirst("^get", "");
|
||||
if(nameWithoutGet.length() == 1)
|
||||
{
|
||||
return (nameWithoutGet.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return (nameWithoutGet.substring(0, 1).toLowerCase(Locale.ROOT) + nameWithoutGet.substring(1));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean isGetter(Method method)
|
||||
{
|
||||
if(method.getParameterTypes().length == 0 && method.getName().matches("^get[A-Z].*"))
|
||||
{
|
||||
if(isSupportedFieldType(method.getReturnType()))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!method.getName().equals("getClass"))
|
||||
{
|
||||
System.err.println("Method [" + method.getName() + "] looks like a getter, but its return type, [" + method.getReturnType() + "], isn't supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean isSupportedFieldType(Class<?> returnType)
|
||||
{
|
||||
// todo - more types!!
|
||||
return (returnType.equals(String.class)
|
||||
|| returnType.equals(Integer.class)
|
||||
|| returnType.equals(int.class)
|
||||
|| returnType.equals(Boolean.class)
|
||||
|| returnType.equals(boolean.class)
|
||||
|| returnType.equals(BigDecimal.class)
|
||||
|| returnType.equals(Instant.class)
|
||||
|| returnType.equals(LocalDate.class)
|
||||
|| returnType.equals(LocalTime.class));
|
||||
/////////////////////////////////////////////
|
||||
// note - this list has implications upon: //
|
||||
// - QFieldType.fromClass //
|
||||
// - QRecordEntityField.convertValueType //
|
||||
/////////////////////////////////////////////
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,13 @@
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
@ -38,6 +43,9 @@ public class QBackendMetaData
|
||||
private String name;
|
||||
private String backendType;
|
||||
|
||||
private Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
private Set<Capability> disabledCapabilities = new HashSet<>();
|
||||
|
||||
// todo - at some point, we may want to apply this to secret properties on subclasses?
|
||||
// @JsonFilter("secretsFilter")
|
||||
|
||||
@ -157,4 +165,172 @@ public class QBackendMetaData
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getEnabledCapabilities()
|
||||
{
|
||||
return enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapability(Capability capability)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getDisabledCapabilities()
|
||||
{
|
||||
return disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QBackendMetaData withoutCapability(Capability capability)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void performValidation(QInstanceValidator qInstanceValidator)
|
||||
{
|
||||
////////////////////////
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.code;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AssociatedScriptCodeReference extends QCodeReference
|
||||
{
|
||||
private String recordTable;
|
||||
private Serializable recordPrimaryKey;
|
||||
private String fieldName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getRecordTable()
|
||||
{
|
||||
return recordTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRecordTable(String recordTable)
|
||||
{
|
||||
this.recordTable = recordTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScriptCodeReference withRecordTable(String recordTable)
|
||||
{
|
||||
this.recordTable = recordTable;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getRecordPrimaryKey()
|
||||
{
|
||||
return recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for recordPrimaryKey
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScriptCodeReference withRecordPrimaryKey(Serializable recordPrimaryKey)
|
||||
{
|
||||
this.recordPrimaryKey = recordPrimaryKey;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScriptCodeReference withFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -38,6 +38,8 @@ public class QCodeReference implements Serializable
|
||||
private QCodeType codeType;
|
||||
private QCodeUsage codeUsage;
|
||||
|
||||
private String inlineCode;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -212,4 +214,38 @@ public class QCodeReference implements Serializable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for inlineCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getInlineCode()
|
||||
{
|
||||
return inlineCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for inlineCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInlineCode(String inlineCode)
|
||||
{
|
||||
this.inlineCode = inlineCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for inlineCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QCodeReference withInlineCode(String inlineCode)
|
||||
{
|
||||
this.inlineCode = inlineCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,5 +28,6 @@ package com.kingsrook.qqq.backend.core.model.metadata.code;
|
||||
*******************************************************************************/
|
||||
public enum QCodeType
|
||||
{
|
||||
JAVA
|
||||
JAVA,
|
||||
JAVA_SCRIPT
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.dashboard;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
|
||||
|
||||
@ -38,6 +41,8 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
protected Integer gridColumns;
|
||||
protected QCodeReference codeReference;
|
||||
|
||||
protected Map<String, Serializable> defaultValues = new LinkedHashMap<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -242,4 +247,56 @@ public class QWidgetMetaData implements QWidgetMetaDataInterface
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getDefaultValues()
|
||||
{
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDefaultValues(Map<String, Serializable> defaultValues)
|
||||
{
|
||||
this.defaultValues = defaultValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultValues
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QWidgetMetaData withDefaultValues(Map<String, Serializable> defaultValues)
|
||||
{
|
||||
this.defaultValues = defaultValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for a single defaultValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QWidgetMetaData withDefaultValue(String key, Serializable value)
|
||||
{
|
||||
if(this.defaultValues == null)
|
||||
{
|
||||
this.defaultValues = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
this.defaultValues.put(key, value);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ public enum AdornmentType
|
||||
{
|
||||
LINK,
|
||||
CHIP,
|
||||
SIZE;
|
||||
SIZE,
|
||||
CODE_EDITOR;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
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.utils.CollectionUtils;
|
||||
@ -53,6 +58,8 @@ public class QFrontendTableMetaData
|
||||
|
||||
private List<String> widgets;
|
||||
|
||||
private Set<String> capabilities;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -62,7 +69,7 @@ public class QFrontendTableMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendTableMetaData(QTableMetaData tableMetaData, boolean includeFields)
|
||||
public QFrontendTableMetaData(QBackendMetaData backendForTable, QTableMetaData tableMetaData, boolean includeFields)
|
||||
{
|
||||
this.name = tableMetaData.getName();
|
||||
this.label = tableMetaData.getLabel();
|
||||
@ -89,6 +96,62 @@ public class QFrontendTableMetaData
|
||||
{
|
||||
this.widgets = tableMetaData.getWidgets();
|
||||
}
|
||||
|
||||
setCapabilities(backendForTable, tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void setCapabilities(QBackendMetaData backend, QTableMetaData table)
|
||||
{
|
||||
Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
for(Capability capability : Capability.values())
|
||||
{
|
||||
///////////////////////////////////////////////
|
||||
// by default, every table can do everything //
|
||||
///////////////////////////////////////////////
|
||||
boolean hasCapability = true;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the table's backend says the capability is disabled, then by default, then the capability is disabled... //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(backend.getDisabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = false;
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// unless the table overrides that and says that it IS enabled //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if(table.getEnabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the backend doesn't specify the capability, then disable it if the table says so //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(table.getDisabledCapabilities().contains(capability))
|
||||
{
|
||||
hasCapability = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasCapability)
|
||||
{
|
||||
///////////////////////////////////////
|
||||
// todo - check if user is allowed!! //
|
||||
///////////////////////////////////////
|
||||
|
||||
enabledCapabilities.add(capability);
|
||||
}
|
||||
}
|
||||
|
||||
this.capabilities = enabledCapabilities.stream().map(Enum::name).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
@ -178,4 +241,16 @@ public class QFrontendTableMetaData
|
||||
{
|
||||
return widgets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for capabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<String> getCapabilities()
|
||||
{
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
@ -340,4 +343,38 @@ public class QAppMetaData implements QAppChildMetaData
|
||||
this.addSection(section);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppMetaData withSectionOfChildren(QAppSection section, QAppChildMetaData... children)
|
||||
{
|
||||
this.addSection(section);
|
||||
|
||||
for(QAppChildMetaData child : children)
|
||||
{
|
||||
withChild(child);
|
||||
if(child instanceof QTableMetaData)
|
||||
{
|
||||
section.withTable(child.getName());
|
||||
}
|
||||
else if(child instanceof QProcessMetaData)
|
||||
{
|
||||
section.withProcess(child.getName());
|
||||
}
|
||||
else if(child instanceof QReportMetaData)
|
||||
{
|
||||
section.withReport(child.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized child type: " + child.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@ -166,6 +167,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tables
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withTable(String tableName)
|
||||
{
|
||||
if(this.tables == null)
|
||||
{
|
||||
this.tables = new ArrayList<>();
|
||||
}
|
||||
this.tables.add(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processes
|
||||
**
|
||||
@ -200,6 +217,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processes
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withProcess(String processName)
|
||||
{
|
||||
if(this.processes == null)
|
||||
{
|
||||
this.processes = new ArrayList<>();
|
||||
}
|
||||
this.processes.add(processName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for reports
|
||||
**
|
||||
@ -234,6 +267,22 @@ public class QAppSection
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for reports
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QAppSection withReport(String reportName)
|
||||
{
|
||||
if(this.reports == null)
|
||||
{
|
||||
this.reports = new ArrayList<>();
|
||||
}
|
||||
this.reports.add(reportName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for icon
|
||||
**
|
||||
|
@ -34,6 +34,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
public class QIcon
|
||||
{
|
||||
private String name;
|
||||
private String path;
|
||||
|
||||
|
||||
|
||||
@ -88,4 +89,38 @@ public class QIcon
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for path
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QIcon withPath(String path)
|
||||
{
|
||||
this.path = path;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -289,6 +289,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.type = QPossibleValueSourceType.TABLE;
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
@ -300,7 +301,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
setTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -446,6 +447,7 @@ public class QPossibleValueSource
|
||||
public void setEnumValues(List<QPossibleValue<?>> enumValues)
|
||||
{
|
||||
this.enumValues = enumValues;
|
||||
setType(QPossibleValueSourceType.ENUM);
|
||||
}
|
||||
|
||||
|
||||
@ -456,7 +458,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withEnumValues(List<QPossibleValue<?>> enumValues)
|
||||
{
|
||||
this.enumValues = enumValues;
|
||||
setEnumValues(enumValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -472,6 +474,7 @@ public class QPossibleValueSource
|
||||
this.enumValues = new ArrayList<>();
|
||||
}
|
||||
this.enumValues.add(possibleValue);
|
||||
setType(QPossibleValueSourceType.ENUM);
|
||||
}
|
||||
|
||||
|
||||
@ -512,6 +515,7 @@ public class QPossibleValueSource
|
||||
public void setCustomCodeReference(QCodeReference customCodeReference)
|
||||
{
|
||||
this.customCodeReference = customCodeReference;
|
||||
setType(QPossibleValueSourceType.CUSTOM);
|
||||
}
|
||||
|
||||
|
||||
@ -522,7 +526,7 @@ public class QPossibleValueSource
|
||||
*******************************************************************************/
|
||||
public QPossibleValueSource withCustomCodeReference(QCodeReference customCodeReference)
|
||||
{
|
||||
this.customCodeReference = customCodeReference;
|
||||
setCustomCodeReference(customCodeReference);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AbstractProcessMetaDataBuilder
|
||||
{
|
||||
protected QProcessMetaData processMetaData;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AbstractProcessMetaDataBuilder(QProcessMetaData processMetaData)
|
||||
{
|
||||
this.processMetaData = processMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processMetaData
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData getProcessMetaData()
|
||||
{
|
||||
return processMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void setInputFieldDefaultValue(String fieldName, Serializable value)
|
||||
{
|
||||
processMetaData.getInputFields().stream()
|
||||
.filter(f -> f.getName().equals(fieldName)).findFirst()
|
||||
.ifPresent(f -> f.setDefaultValue(value));
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -41,10 +42,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaDa
|
||||
*******************************************************************************/
|
||||
public class QProcessMetaData implements QAppChildMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
private boolean isHidden = false;
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
private boolean isHidden = false;
|
||||
private BasepullConfiguration basepullConfiguration;
|
||||
|
||||
private List<QStepMetaData> stepList; // these are the steps that are ran, by-default, in the order they are ran in
|
||||
private Map<String, QStepMetaData> steps; // this is the full map of possible steps
|
||||
@ -473,4 +475,38 @@ public class QProcessMetaData implements QAppChildMetaData
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for basepullConfiguration
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration getBasepullConfiguration()
|
||||
{
|
||||
return basepullConfiguration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for basepullConfiguration
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBasepullConfiguration(BasepullConfiguration basepullConfiguration)
|
||||
{
|
||||
this.basepullConfiguration = basepullConfiguration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for basepullConfiguration
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QProcessMetaData withBasepullConfiguration(BasepullConfiguration basepullConfiguration)
|
||||
{
|
||||
this.basepullConfiguration = basepullConfiguration;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class AssociatedScript implements Serializable
|
||||
{
|
||||
private String fieldName;
|
||||
private Serializable scriptTypeId;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScript withFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getScriptTypeId()
|
||||
{
|
||||
return scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptTypeId(Serializable scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public AssociatedScript withScriptTypeId(Serializable scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Things that can be done to tables, fields.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum Capability
|
||||
{
|
||||
TABLE_QUERY,
|
||||
TABLE_GET,
|
||||
TABLE_COUNT,
|
||||
TABLE_INSERT,
|
||||
TABLE_UPDATE,
|
||||
TABLE_DELETE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
@ -38,6 +38,8 @@ public class QFieldSection
|
||||
private List<String> fieldNames;
|
||||
private QIcon icon;
|
||||
|
||||
private boolean isHidden = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -244,4 +246,38 @@ public class QFieldSection
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getIsHidden()
|
||||
{
|
||||
return (isHidden);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setIsHidden(boolean isHidden)
|
||||
{
|
||||
this.isHidden = isHidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent Setter for isHidden
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldSection withIsHidden(boolean isHidden)
|
||||
{
|
||||
this.isHidden = isHidden;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,10 +26,12 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
@ -78,7 +80,11 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
|
||||
private List<QFieldSection> sections;
|
||||
|
||||
private List<String> widgets;
|
||||
private List<String> widgets;
|
||||
private List<AssociatedScript> associatedScripts;
|
||||
|
||||
private Set<Capability> enabledCapabilities = new HashSet<>();
|
||||
private Set<Capability> disabledCapabilities = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
@ -754,6 +760,56 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for associatedScripts
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<AssociatedScript> getAssociatedScripts()
|
||||
{
|
||||
return associatedScripts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for associatedScripts
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setAssociatedScripts(List<AssociatedScript> associatedScripts)
|
||||
{
|
||||
this.associatedScripts = associatedScripts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for associatedScripts
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withAssociatedScripts(List<AssociatedScript> associatedScripts)
|
||||
{
|
||||
this.associatedScripts = associatedScripts;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for associatedScripts
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withAssociatedScript(AssociatedScript associatedScript)
|
||||
{
|
||||
if(this.associatedScripts == null)
|
||||
{
|
||||
this.associatedScripts = new ArrayList();
|
||||
}
|
||||
this.associatedScripts.add(associatedScript);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for uniqueKeys
|
||||
**
|
||||
@ -802,4 +858,181 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluently add a section and fields in that section.
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withSectionOfFields(QFieldSection fieldSection, QFieldMetaData... fields)
|
||||
{
|
||||
withSection(fieldSection);
|
||||
|
||||
List<String> fieldNames = new ArrayList<>();
|
||||
for(QFieldMetaData field : fields)
|
||||
{
|
||||
withField(field);
|
||||
fieldNames.add(field.getName());
|
||||
}
|
||||
|
||||
fieldSection.setFieldNames(fieldNames);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getEnabledCapabilities()
|
||||
{
|
||||
return enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||
{
|
||||
this.enabledCapabilities = enabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapability(Capability capability)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withCapabilities(Capability... enabledCapabilities)
|
||||
{
|
||||
if(this.enabledCapabilities == null)
|
||||
{
|
||||
this.enabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<Capability> getDisabledCapabilities()
|
||||
{
|
||||
return disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||
{
|
||||
this.disabledCapabilities = disabledCapabilities;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Alternative fluent setter for a single disabledCapabilities
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withoutCapability(Capability capability)
|
||||
{
|
||||
if(this.disabledCapabilities == null)
|
||||
{
|
||||
this.disabledCapabilities = new HashSet<>();
|
||||
}
|
||||
this.disabledCapabilities.add(capability);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class Script extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "script";
|
||||
|
||||
@QField()
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant createDate;
|
||||
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField()
|
||||
private String name;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptType")
|
||||
private Integer scriptTypeId;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptRevision")
|
||||
private Integer currentScriptRevisionId;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getScriptTypeId()
|
||||
{
|
||||
return scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptTypeId(Integer scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptTypeId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withScriptTypeId(Integer scriptTypeId)
|
||||
{
|
||||
this.scriptTypeId = scriptTypeId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for currentScriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getCurrentScriptRevisionId()
|
||||
{
|
||||
return currentScriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for currentScriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCurrentScriptRevisionId(Integer currentScriptRevisionId)
|
||||
{
|
||||
this.currentScriptRevisionId = currentScriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for currentScriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Script withCurrentScriptRevisionId(Integer currentScriptRevisionId)
|
||||
{
|
||||
this.currentScriptRevisionId = currentScriptRevisionId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,482 @@
|
||||
/*
|
||||
* 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.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptLog extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptLog";
|
||||
|
||||
@QField()
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant createDate;
|
||||
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = "script")
|
||||
private Integer scriptId;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptRevision")
|
||||
private Integer scriptRevisionId;
|
||||
|
||||
@QField()
|
||||
private Instant startTimestamp;
|
||||
|
||||
@QField()
|
||||
private Instant endTimestamp;
|
||||
|
||||
@QField(displayFormat = DisplayFormat.COMMAS)
|
||||
private Integer runTimeMillis;
|
||||
|
||||
@QField()
|
||||
private Boolean hadError;
|
||||
|
||||
@QField()
|
||||
private String input;
|
||||
|
||||
@QField()
|
||||
private String output;
|
||||
|
||||
@QField()
|
||||
private String error;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getScriptId()
|
||||
{
|
||||
return scriptId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptId(Integer scriptId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withScriptId(Integer scriptId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getScriptRevisionId()
|
||||
{
|
||||
return scriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptRevisionId(Integer scriptRevisionId)
|
||||
{
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptRevisionId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withScriptRevisionId(Integer scriptRevisionId)
|
||||
{
|
||||
this.scriptRevisionId = scriptRevisionId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for startTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getStartTimestamp()
|
||||
{
|
||||
return startTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for startTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setStartTimestamp(Instant startTimestamp)
|
||||
{
|
||||
this.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for startTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withStartTimestamp(Instant startTimestamp)
|
||||
{
|
||||
this.startTimestamp = startTimestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for endTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getEndTimestamp()
|
||||
{
|
||||
return endTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for endTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEndTimestamp(Instant endTimestamp)
|
||||
{
|
||||
this.endTimestamp = endTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for endTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withEndTimestamp(Instant endTimestamp)
|
||||
{
|
||||
this.endTimestamp = endTimestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for runTimeMillis
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getRunTimeMillis()
|
||||
{
|
||||
return runTimeMillis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for runTimeMillis
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setRunTimeMillis(Integer runTimeMillis)
|
||||
{
|
||||
this.runTimeMillis = runTimeMillis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for runTimeMillis
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withRunTimeMillis(Integer runTimeMillis)
|
||||
{
|
||||
this.runTimeMillis = runTimeMillis;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hadError
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Boolean getHadError()
|
||||
{
|
||||
return hadError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hadError
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setHadError(Boolean hadError)
|
||||
{
|
||||
this.hadError = hadError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hadError
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withHadError(Boolean hadError)
|
||||
{
|
||||
this.hadError = hadError;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getInput()
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setInput(String input)
|
||||
{
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for input
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withInput(String input)
|
||||
{
|
||||
this.input = input;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getOutput()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setOutput(String output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for output
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withOutput(String output)
|
||||
{
|
||||
this.output = output;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for error
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for error
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for error
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLog withError(String error)
|
||||
{
|
||||
this.error = error;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptLogLine extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptLogLine";
|
||||
|
||||
@QField()
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant createDate;
|
||||
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = "scriptLog")
|
||||
private Integer scriptLogId;
|
||||
|
||||
@QField()
|
||||
private Instant timestamp;
|
||||
|
||||
@QField()
|
||||
private String text;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptLogId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getScriptLogId()
|
||||
{
|
||||
return scriptLogId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptLogId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptLogId(Integer scriptLogId)
|
||||
{
|
||||
this.scriptLogId = scriptLogId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptLogId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withScriptLogId(Integer scriptLogId)
|
||||
{
|
||||
this.scriptLogId = scriptLogId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getTimestamp()
|
||||
{
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTimestamp(Instant timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withTimestamp(Instant timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for text
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for text
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setText(String text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for text
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptLogLine withText(String text)
|
||||
{
|
||||
this.text = text;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptRevision extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptRevision";
|
||||
|
||||
@QField()
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant createDate;
|
||||
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = "script")
|
||||
private Integer scriptId;
|
||||
|
||||
@QField()
|
||||
private String contents;
|
||||
|
||||
@QField()
|
||||
private Integer sequenceNo;
|
||||
|
||||
@QField()
|
||||
private String commitMessage;
|
||||
|
||||
@QField()
|
||||
private String author;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getScriptId()
|
||||
{
|
||||
return scriptId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setScriptId(Integer scriptId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for scriptId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withScriptId(Integer scriptId)
|
||||
{
|
||||
this.scriptId = scriptId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getSequenceNo()
|
||||
{
|
||||
return sequenceNo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sequenceNo
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSequenceNo(Integer sequenceNo)
|
||||
{
|
||||
this.sequenceNo = sequenceNo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sequenceNo
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withSequenceNo(Integer sequenceNo)
|
||||
{
|
||||
this.sequenceNo = sequenceNo;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getCommitMessage()
|
||||
{
|
||||
return commitMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCommitMessage(String commitMessage)
|
||||
{
|
||||
this.commitMessage = commitMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for commitMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withCommitMessage(String commitMessage)
|
||||
{
|
||||
this.commitMessage = commitMessage;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for author
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getAuthor()
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for author
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setAuthor(String author)
|
||||
{
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for author
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptRevision withAuthor(String author)
|
||||
{
|
||||
this.author = author;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptType extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "scriptType";
|
||||
|
||||
@QField()
|
||||
private Integer id;
|
||||
|
||||
@QField()
|
||||
private Instant createDate;
|
||||
|
||||
@QField()
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField()
|
||||
private String name;
|
||||
|
||||
@QField()
|
||||
private String helpText;
|
||||
|
||||
@QField()
|
||||
private String sampleCode;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for id
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getCreateDate()
|
||||
{
|
||||
return createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for createDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Instant getModifyDate()
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for modifyDate
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for helpText
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getHelpText()
|
||||
{
|
||||
return helpText;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for helpText
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setHelpText(String helpText)
|
||||
{
|
||||
this.helpText = helpText;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for helpText
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withHelpText(String helpText)
|
||||
{
|
||||
this.helpText = helpText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sampleCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getSampleCode()
|
||||
{
|
||||
return sampleCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sampleCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSampleCode(String sampleCode)
|
||||
{
|
||||
this.sampleCode = sampleCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sampleCode
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ScriptType withSampleCode(String sampleCode)
|
||||
{
|
||||
this.sampleCode = sampleCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
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.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
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.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.Tier;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class ScriptsMetaDataProvider
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
defineStandardScriptsTables(instance, backendName, backendDetailEnricher);
|
||||
defineStandardScriptsPossibleValueSources(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineStandardScriptsTables(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
for(QTableMetaData tableMetaData : defineStandardScriptsTables(backendName, backendDetailEnricher))
|
||||
{
|
||||
instance.addTable(tableMetaData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void defineStandardScriptsPossibleValueSources(QInstance instance) throws QException
|
||||
{
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(Script.TABLE_NAME)
|
||||
.withTableName(Script.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptRevision.TABLE_NAME)
|
||||
.withTableName(ScriptRevision.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptType.TABLE_NAME)
|
||||
.withTableName(ScriptType.TABLE_NAME)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(ScriptLog.TABLE_NAME)
|
||||
.withTableName(ScriptLog.TABLE_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private List<QTableMetaData> defineStandardScriptsTables(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
List<QTableMetaData> rs = new ArrayList<>();
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTypeTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptRevisionTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptLogTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineScriptLogLineTable(backendName)));
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData enrich(Consumer<QTableMetaData> backendDetailEnricher, QTableMetaData table)
|
||||
{
|
||||
if(backendDetailEnricher != null)
|
||||
{
|
||||
backendDetailEnricher.accept(table);
|
||||
}
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineStandardTable(String backendName, String name, Class<? extends QRecordEntity> fieldsFromEntity) throws QException
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName(name)
|
||||
.withBackendName(backendName)
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("name")
|
||||
.withPrimaryKeyField("id")
|
||||
.withFieldsFromEntity(fieldsFromEntity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, Script.TABLE_NAME, Script.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId", "currentScriptRevisionId")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate"))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptTypeTable(String backendName) throws QException
|
||||
{
|
||||
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("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
tableMetaData.getField("sampleCode").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR));
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptRevisionTable(String backendName) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, ScriptRevision.TABLE_NAME, ScriptRevision.class)
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE)
|
||||
.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("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")));
|
||||
|
||||
tableMetaData.getField("contents").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR));
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptLogTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, ScriptLog.TABLE_NAME, ScriptLog.class)
|
||||
.withRecordLabelFields(List.of("id"))
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id")))
|
||||
.withSection(new QFieldSection("script", new QIcon().withName("data_object"), Tier.T2, List.of("scriptId", "scriptRevisionId")))
|
||||
.withSection(new QFieldSection("timing", new QIcon().withName("schedule"), Tier.T2, List.of("startTimestamp", "endTimestamp", "runTimeMillis", "createDate", "modifyDate")))
|
||||
.withSection(new QFieldSection("error", "Error", new QIcon().withName("error_outline"), Tier.T2, List.of("hadError", "error")))
|
||||
.withSection(new QFieldSection("inputOutput", "Input/Output", new QIcon().withName("chat"), Tier.T2, List.of("input", "output"))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineScriptLogLineTable(String backendName) throws QException
|
||||
{
|
||||
return (defineStandardTable(backendName, ScriptLogLine.TABLE_NAME, ScriptLogLine.class)
|
||||
.withRecordLabelFields(List.of("id")));
|
||||
}
|
||||
|
||||
}
|
@ -22,12 +22,16 @@
|
||||
package com.kingsrook.qqq.backend.core.modules.authentication;
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import com.auth0.client.auth.AuthAPI;
|
||||
import com.auth0.exception.Auth0Exception;
|
||||
import com.auth0.json.auth.TokenHolder;
|
||||
import com.auth0.jwk.Jwk;
|
||||
import com.auth0.jwk.JwkException;
|
||||
import com.auth0.jwk.JwkProvider;
|
||||
@ -65,6 +69,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
public static final int ID_TOKEN_VALIDATION_INTERVAL_SECONDS = 1800;
|
||||
|
||||
public static final String AUTH0_ID_TOKEN_KEY = "sessionId";
|
||||
public static final String BASIC_AUTH_KEY = "basicAuthString";
|
||||
|
||||
public static final String TOKEN_NOT_PROVIDED_ERROR = "Id Token was not provided";
|
||||
public static final String COULD_NOT_DECODE_ERROR = "Unable to decode id token";
|
||||
@ -82,6 +87,43 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
@Override
|
||||
public QSession createSession(QInstance qInstance, Map<String, String> context) throws QAuthenticationException
|
||||
{
|
||||
///////////////////////////////////////////////////////////
|
||||
// check if we are processing a Basic Auth Session first //
|
||||
///////////////////////////////////////////////////////////
|
||||
if(context.containsKey(BASIC_AUTH_KEY))
|
||||
{
|
||||
Auth0AuthenticationMetaData metaData = (Auth0AuthenticationMetaData) qInstance.getAuthentication();
|
||||
AuthAPI auth = new AuthAPI(metaData.getBaseUrl(), metaData.getClientId(), metaData.getClientSecret());
|
||||
try
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// decode the credentials from the header auth //
|
||||
/////////////////////////////////////////////////
|
||||
String base64Credentials = context.get(BASIC_AUTH_KEY).trim();
|
||||
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
|
||||
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
|
||||
|
||||
/////////////////////////////////////
|
||||
// call auth0 with a login request //
|
||||
/////////////////////////////////////
|
||||
TokenHolder result = auth.login(credentials.split(":")[0], credentials.split(":")[1].toCharArray())
|
||||
.setScope("openid email nickname")
|
||||
.execute();
|
||||
|
||||
context.put(AUTH0_ID_TOKEN_KEY, result.getIdToken());
|
||||
}
|
||||
catch(Auth0Exception e)
|
||||
{
|
||||
////////////////
|
||||
// ¯\_(ツ)_/¯ //
|
||||
////////////////
|
||||
String message = "An unknown error occurred during handling basic auth";
|
||||
LOG.error(message, e);
|
||||
throw (new QAuthenticationException(message));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// get the jwt id token from the context object //
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -31,6 +31,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
|
||||
{
|
||||
private String baseUrl;
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
|
||||
|
||||
|
||||
@ -76,4 +78,69 @@ public class Auth0AuthenticationMetaData extends QAuthenticationMetaData
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter, override to help fluent flows
|
||||
*******************************************************************************/
|
||||
public Auth0AuthenticationMetaData withClientId(String clientId)
|
||||
{
|
||||
setClientId(clientId);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for clientId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getClientId()
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for clientId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setClientId(String clientId)
|
||||
{
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter, override to help fluent flows
|
||||
*******************************************************************************/
|
||||
public Auth0AuthenticationMetaData withClientSecret(String clientSecret)
|
||||
{
|
||||
setClientSecret(clientSecret);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for clientSecret
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getClientSecret()
|
||||
{
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for clientSecret
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setClientSecret(String clientSecret)
|
||||
{
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ public class QBackendModuleDispatcher
|
||||
// e.g., backend-core shouldn't need to "know" about the modules.
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockBackendModule",
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule",
|
||||
"com.kingsrook.qqq.backend.core.modules.backend.implementations.enumeration.EnumerationBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.local.FilesystemBackendModule",
|
||||
"com.kingsrook.qqq.backend.module.filesystem.s3.S3BackendModule",
|
||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.modules.backend;
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.GetInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface;
|
||||
@ -48,7 +49,10 @@ public interface QBackendModuleInterface
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
Class<? extends QBackendMetaData> getBackendMetaDataClass();
|
||||
default Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (QBackendMetaData.class);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for table-backend details for this module.
|
||||
@ -76,6 +80,15 @@ public interface QBackendModuleInterface
|
||||
return null;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default GetInterface getGetInterface()
|
||||
{
|
||||
throwNotImplemented("Get");
|
||||
return null;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.modules.backend.implementations.enumeration;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Backend module for a table based on a java enum. So we can expose an enum
|
||||
** as a table (similar to exposing an enum as a possible value source), with multiple
|
||||
** fields in the enum (exposed via getter methods in the enum) as fields in the table.
|
||||
**
|
||||
** Only supports read-operations, as you can't modify an enum.
|
||||
*******************************************************************************/
|
||||
public class EnumerationBackendModule implements QBackendModuleInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Method where a backend module must be able to provide its type (name).
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getBackendType()
|
||||
{
|
||||
return ("enum");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QTableBackendDetails> getTableBackendDetailsClass()
|
||||
{
|
||||
return (EnumerationTableBackendDetails.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QueryInterface getQueryInterface()
|
||||
{
|
||||
return new EnumerationQueryAction();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.modules.backend.implementations.enumeration;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.data.QRecordEnum;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class EnumerationQueryAction implements QueryInterface
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QueryOutput execute(QueryInput queryInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
QTableMetaData table = queryInput.getTable();
|
||||
EnumerationTableBackendDetails backendDetails = (EnumerationTableBackendDetails) table.getBackendDetails();
|
||||
Class<? extends QRecordEnum> enumClass = backendDetails.getEnumClass();
|
||||
QRecordEnum[] values = (QRecordEnum[]) enumClass.getMethod("values").invoke(null);
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// note - not good streaming behavior here. //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
List<QRecord> recordList = new ArrayList<>();
|
||||
for(QRecordEnum value : values)
|
||||
{
|
||||
QRecord record = value.toQRecord();
|
||||
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(queryInput.getFilter(), record);
|
||||
if(recordMatches)
|
||||
{
|
||||
recordList.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
BackendQueryFilterUtils.sortRecordList(queryInput.getFilter(), recordList);
|
||||
recordList = BackendQueryFilterUtils.applySkipAndLimit(queryInput, recordList);
|
||||
|
||||
QueryOutput queryOutput = new QueryOutput(queryInput);
|
||||
queryOutput.addRecords(recordList);
|
||||
return queryOutput;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error executing query", e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.modules.backend.implementations.enumeration;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEnum;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class EnumerationTableBackendDetails extends QTableBackendDetails
|
||||
{
|
||||
private Class<? extends QRecordEnum> enumClass;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public EnumerationTableBackendDetails()
|
||||
{
|
||||
super();
|
||||
setBackendType(EnumerationBackendModule.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for enumClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<? extends QRecordEnum> getEnumClass()
|
||||
{
|
||||
return enumClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for enumClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setEnumClass(Class<? extends QRecordEnum> enumClass)
|
||||
{
|
||||
this.enumClass = enumClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for enumClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public EnumerationTableBackendDetails withEnumClass(Class<? extends QRecordEnum> enumClass)
|
||||
{
|
||||
this.enumClass = enumClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -27,7 +27,6 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
|
||||
|
||||
@ -53,17 +52,6 @@ public class MemoryBackendModule implements QBackendModuleInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Method to identify the class used for backend meta data for this module.
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
|
||||
{
|
||||
return (QBackendMetaData.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -23,27 +23,21 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.memory;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
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.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
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.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -126,7 +120,7 @@ public class MemoryRecordStore
|
||||
|
||||
for(QRecord qRecord : tableData.values())
|
||||
{
|
||||
boolean recordMatches = doesRecordMatch(input.getFilter(), qRecord);
|
||||
boolean recordMatches = BackendQueryFilterUtils.doesRecordMatch(input.getFilter(), qRecord);
|
||||
|
||||
if(recordMatches)
|
||||
{
|
||||
@ -139,349 +133,6 @@ public class MemoryRecordStore
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
private boolean doesRecordMatch(QQueryFilter filter, QRecord qRecord)
|
||||
{
|
||||
if(filter == null || !filter.hasAnyCriteria())
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// for an AND query, default to a TRUE answer, and we'll &= each criteria's value. //
|
||||
// for an OR query, default to FALSE, and |= each criteria's value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
AtomicBoolean recordMatches = new AtomicBoolean(filter.getBooleanOperator().equals(QQueryFilter.BooleanOperator.AND) ? true : false);
|
||||
|
||||
///////////////////////////////////////
|
||||
// if there are criteria, apply them //
|
||||
///////////////////////////////////////
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(filter.getCriteria()))
|
||||
{
|
||||
String fieldName = criterion.getFieldName();
|
||||
Serializable value = qRecord.getValue(fieldName);
|
||||
|
||||
boolean criterionMatches = switch(criterion.getOperator())
|
||||
{
|
||||
case EQUALS -> testEquals(criterion, value);
|
||||
case NOT_EQUALS -> !testEquals(criterion, value);
|
||||
case IN -> testIn(criterion, value);
|
||||
case NOT_IN -> !testIn(criterion, value);
|
||||
case IS_BLANK -> testBlank(criterion, value);
|
||||
case IS_NOT_BLANK -> !testBlank(criterion, value);
|
||||
case CONTAINS -> testContains(criterion, fieldName, value);
|
||||
case NOT_CONTAINS -> !testContains(criterion, fieldName, value);
|
||||
case STARTS_WITH -> testStartsWith(criterion, fieldName, value);
|
||||
case NOT_STARTS_WITH -> !testStartsWith(criterion, fieldName, value);
|
||||
case ENDS_WITH -> testEndsWith(criterion, fieldName, value);
|
||||
case NOT_ENDS_WITH -> !testEndsWith(criterion, fieldName, value);
|
||||
case GREATER_THAN -> testGreaterThan(criterion, value);
|
||||
case GREATER_THAN_OR_EQUALS -> testGreaterThan(criterion, value) || testEquals(criterion, value);
|
||||
case LESS_THAN -> !testGreaterThan(criterion, value) && !testEquals(criterion, value);
|
||||
case LESS_THAN_OR_EQUALS -> !testGreaterThan(criterion, value);
|
||||
case BETWEEN ->
|
||||
{
|
||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(new ArrayList<>(criterion.getValues()));
|
||||
criteria1.getValues().remove(0);
|
||||
yield (testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||
}
|
||||
case NOT_BETWEEN ->
|
||||
{
|
||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
criteria1.getValues().remove(0);
|
||||
yield !(testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Boolean shortCircuitValue = applyBooleanOperator(recordMatches, criterionMatches, filter.getBooleanOperator());
|
||||
if(shortCircuitValue != null)
|
||||
{
|
||||
return (shortCircuitValue);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// apply sub-filters if there are any //
|
||||
////////////////////////////////////////
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(filter.getSubFilters()))
|
||||
{
|
||||
boolean subFilterMatches = doesRecordMatch(subFilter, qRecord);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Boolean shortCircuitValue = applyBooleanOperator(recordMatches, subFilterMatches, filter.getBooleanOperator());
|
||||
if(shortCircuitValue != null)
|
||||
{
|
||||
return (shortCircuitValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (recordMatches.getPlain());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Based on an incoming boolean value (accumulator), a new value, and a boolean
|
||||
** operator, update the accumulator, and if we can then short-circuit remaining
|
||||
** operations, return a true or false. Returning null means to keep going.
|
||||
*******************************************************************************/
|
||||
private Boolean applyBooleanOperator(AtomicBoolean accumulator, boolean newValue, QQueryFilter.BooleanOperator booleanOperator)
|
||||
{
|
||||
boolean accumulatorValue = accumulator.getPlain();
|
||||
if(booleanOperator.equals(QQueryFilter.BooleanOperator.AND))
|
||||
{
|
||||
accumulatorValue &= newValue;
|
||||
if(!accumulatorValue)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accumulatorValue |= newValue;
|
||||
if(accumulatorValue)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
accumulator.set(accumulatorValue);
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testBlank(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
if("".equals(ValueUtils.getValueAsString(value)))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testGreaterThan(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
Serializable criterionValue = criterion.getValues().get(0);
|
||||
if(criterionValue == null)
|
||||
{
|
||||
throw (new IllegalArgumentException("Missing criterion value in query"));
|
||||
}
|
||||
|
||||
if(value == null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// a database would say 'false' for if a null column is > a value, so do the same. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
return (false);
|
||||
}
|
||||
|
||||
if(value instanceof LocalDate valueDate && criterionValue instanceof LocalDate criterionValueDate)
|
||||
{
|
||||
return (valueDate.isAfter(criterionValueDate));
|
||||
}
|
||||
|
||||
if(value instanceof Number valueNumber && criterionValue instanceof Number criterionValueNumber)
|
||||
{
|
||||
return (valueNumber.doubleValue() > criterionValueNumber.doubleValue());
|
||||
}
|
||||
|
||||
if(value instanceof LocalDate || criterionValue instanceof LocalDate)
|
||||
{
|
||||
LocalDate valueDate;
|
||||
if(value instanceof LocalDate ld)
|
||||
{
|
||||
valueDate = ld;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueDate = ValueUtils.getValueAsLocalDate(value);
|
||||
}
|
||||
|
||||
LocalDate criterionDate;
|
||||
if(criterionValue instanceof LocalDate ld)
|
||||
{
|
||||
criterionDate = ld;
|
||||
}
|
||||
else
|
||||
{
|
||||
criterionDate = ValueUtils.getValueAsLocalDate(criterionValue);
|
||||
}
|
||||
|
||||
return (valueDate.isAfter(criterionDate));
|
||||
}
|
||||
|
||||
throw (new NotImplementedException("Greater/Less Than comparisons are not (yet?) implemented for the supplied types [" + value.getClass().getSimpleName() + "][" + criterionValue.getClass().getSimpleName() + "]"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testIn(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(!criterion.getValues().contains(value))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testEquals(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
Serializable criteriaValue = criterion.getValues().get(0);
|
||||
if(value instanceof String && criteriaValue instanceof Number)
|
||||
{
|
||||
criteriaValue = String.valueOf(criteriaValue);
|
||||
}
|
||||
else if(criteriaValue instanceof String && value instanceof Number)
|
||||
{
|
||||
value = String.valueOf(value);
|
||||
}
|
||||
|
||||
if(!value.equals(criteriaValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testContains(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.contains(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testStartsWith(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.startsWith(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean testEndsWith(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.endsWith(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getFirstStringCriterionValue(QFilterCriteria criteria)
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(criteria.getValues()))
|
||||
{
|
||||
throw new IllegalArgumentException("Missing value for [" + criteria.getOperator() + "] criteria on field [" + criteria.getFieldName() + "]");
|
||||
}
|
||||
Serializable value = criteria.getValues().get(0);
|
||||
if(value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!(value instanceof String stringValue))
|
||||
{
|
||||
throw new ClassCastException("Value [" + value + "] for criteria [" + criteria.getFieldName() + "] is not a String, which is required for the [" + criteria.getOperator() + "] operator.");
|
||||
}
|
||||
|
||||
return (stringValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getStringFieldValue(Serializable value, String fieldName, QFilterCriteria criterion)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!(value instanceof String stringValue))
|
||||
{
|
||||
throw new ClassCastException("Value [" + value + "] in field [" + fieldName + "] is not a String, which is required for the [" + criterion.getOperator() + "] operator.");
|
||||
}
|
||||
|
||||
return (stringValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,475 @@
|
||||
/*
|
||||
* 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.modules.backend.implementations.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for backend modules that need to do filter operations.
|
||||
**
|
||||
** e.g., like an in-memory module, or one that's working with files - basically
|
||||
** one that doesn't have filtering provided by the backend (like a database or API).
|
||||
*******************************************************************************/
|
||||
public class BackendQueryFilterUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Test if record matches filter.
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("checkstyle:indentation")
|
||||
public static boolean doesRecordMatch(QQueryFilter filter, QRecord qRecord)
|
||||
{
|
||||
if(filter == null || !filter.hasAnyCriteria())
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// for an AND query, default to a TRUE answer, and we'll &= each criteria's value. //
|
||||
// for an OR query, default to FALSE, and |= each criteria's value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
AtomicBoolean recordMatches = new AtomicBoolean(filter.getBooleanOperator().equals(QQueryFilter.BooleanOperator.AND) ? true : false);
|
||||
|
||||
///////////////////////////////////////
|
||||
// if there are criteria, apply them //
|
||||
///////////////////////////////////////
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(filter.getCriteria()))
|
||||
{
|
||||
String fieldName = criterion.getFieldName();
|
||||
Serializable value = qRecord.getValue(fieldName);
|
||||
|
||||
boolean criterionMatches = switch(criterion.getOperator())
|
||||
{
|
||||
case EQUALS -> testEquals(criterion, value);
|
||||
case NOT_EQUALS -> !testEquals(criterion, value);
|
||||
case IN -> testIn(criterion, value);
|
||||
case NOT_IN -> !testIn(criterion, value);
|
||||
case IS_BLANK -> testBlank(criterion, value);
|
||||
case IS_NOT_BLANK -> !testBlank(criterion, value);
|
||||
case CONTAINS -> testContains(criterion, fieldName, value);
|
||||
case NOT_CONTAINS -> !testContains(criterion, fieldName, value);
|
||||
case STARTS_WITH -> testStartsWith(criterion, fieldName, value);
|
||||
case NOT_STARTS_WITH -> !testStartsWith(criterion, fieldName, value);
|
||||
case ENDS_WITH -> testEndsWith(criterion, fieldName, value);
|
||||
case NOT_ENDS_WITH -> !testEndsWith(criterion, fieldName, value);
|
||||
case GREATER_THAN -> testGreaterThan(criterion, value);
|
||||
case GREATER_THAN_OR_EQUALS -> testGreaterThan(criterion, value) || testEquals(criterion, value);
|
||||
case LESS_THAN -> !testGreaterThan(criterion, value) && !testEquals(criterion, value);
|
||||
case LESS_THAN_OR_EQUALS -> !testGreaterThan(criterion, value);
|
||||
case BETWEEN ->
|
||||
{
|
||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(new ArrayList<>(criterion.getValues()));
|
||||
criteria1.getValues().remove(0);
|
||||
yield (testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||
}
|
||||
case NOT_BETWEEN ->
|
||||
{
|
||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(criterion.getValues());
|
||||
criteria1.getValues().remove(0);
|
||||
yield !(testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Boolean shortCircuitValue = applyBooleanOperator(recordMatches, criterionMatches, filter.getBooleanOperator());
|
||||
if(shortCircuitValue != null)
|
||||
{
|
||||
return (shortCircuitValue);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// apply sub-filters if there are any //
|
||||
////////////////////////////////////////
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(filter.getSubFilters()))
|
||||
{
|
||||
boolean subFilterMatches = doesRecordMatch(subFilter, qRecord);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// add this new value to the existing recordMatches value - and if we can short circuit the remaining checks, do so. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Boolean shortCircuitValue = applyBooleanOperator(recordMatches, subFilterMatches, filter.getBooleanOperator());
|
||||
if(shortCircuitValue != null)
|
||||
{
|
||||
return (shortCircuitValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (recordMatches.getPlain());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Based on an incoming boolean value (accumulator), a new value, and a boolean
|
||||
** operator, update the accumulator, and if we can then short-circuit remaining
|
||||
** operations, return a true or false. Returning null means to keep going.
|
||||
*******************************************************************************/
|
||||
private static Boolean applyBooleanOperator(AtomicBoolean accumulator, boolean newValue, QQueryFilter.BooleanOperator booleanOperator)
|
||||
{
|
||||
boolean accumulatorValue = accumulator.getPlain();
|
||||
if(booleanOperator.equals(QQueryFilter.BooleanOperator.AND))
|
||||
{
|
||||
accumulatorValue &= newValue;
|
||||
if(!accumulatorValue)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accumulatorValue |= newValue;
|
||||
if(accumulatorValue)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
accumulator.set(accumulatorValue);
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testBlank(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
if("".equals(ValueUtils.getValueAsString(value)))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testGreaterThan(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
Serializable criterionValue = criterion.getValues().get(0);
|
||||
if(criterionValue == null)
|
||||
{
|
||||
throw (new IllegalArgumentException("Missing criterion value in query"));
|
||||
}
|
||||
|
||||
if(value == null)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// a database would say 'false' for if a null column is > a value, so do the same. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
return (false);
|
||||
}
|
||||
|
||||
return isGreaterThan(criterionValue, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean isGreaterThan(Serializable a, Serializable b)
|
||||
{
|
||||
if(Objects.equals(a, b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(b instanceof LocalDate valueDate && a instanceof LocalDate criterionValueDate)
|
||||
{
|
||||
return (valueDate.isAfter(criterionValueDate));
|
||||
}
|
||||
|
||||
if(b instanceof Number valueNumber && a instanceof Number criterionValueNumber)
|
||||
{
|
||||
return (valueNumber.doubleValue() > criterionValueNumber.doubleValue());
|
||||
}
|
||||
|
||||
if(b instanceof String valueString && a instanceof String criterionValueString)
|
||||
{
|
||||
return (valueString.compareTo(criterionValueString) > 0);
|
||||
}
|
||||
|
||||
if(b instanceof LocalDate || a instanceof LocalDate)
|
||||
{
|
||||
LocalDate valueDate;
|
||||
if(b instanceof LocalDate ld)
|
||||
{
|
||||
valueDate = ld;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueDate = ValueUtils.getValueAsLocalDate(b);
|
||||
}
|
||||
|
||||
LocalDate criterionDate;
|
||||
if(a instanceof LocalDate ld)
|
||||
{
|
||||
criterionDate = ld;
|
||||
}
|
||||
else
|
||||
{
|
||||
criterionDate = ValueUtils.getValueAsLocalDate(a);
|
||||
}
|
||||
|
||||
return (valueDate.isAfter(criterionDate));
|
||||
}
|
||||
|
||||
throw (new NotImplementedException("Greater/Less Than comparisons are not (yet?) implemented for the supplied types [" + b.getClass().getSimpleName() + "][" + a.getClass().getSimpleName() + "]"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testIn(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(!criterion.getValues().contains(value))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testEquals(QFilterCriteria criterion, Serializable value)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
Serializable criteriaValue = criterion.getValues().get(0);
|
||||
if(value instanceof String && criteriaValue instanceof Number)
|
||||
{
|
||||
criteriaValue = String.valueOf(criteriaValue);
|
||||
}
|
||||
else if(criteriaValue instanceof String && value instanceof Number)
|
||||
{
|
||||
value = String.valueOf(value);
|
||||
}
|
||||
|
||||
if(!value.equals(criteriaValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testContains(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.contains(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testStartsWith(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.startsWith(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static boolean testEndsWith(QFilterCriteria criterion, String fieldName, Serializable value)
|
||||
{
|
||||
String stringValue = getStringFieldValue(value, fieldName, criterion);
|
||||
String criterionValue = getFirstStringCriterionValue(criterion);
|
||||
|
||||
if(!stringValue.endsWith(criterionValue))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static String getFirstStringCriterionValue(QFilterCriteria criteria)
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(criteria.getValues()))
|
||||
{
|
||||
throw new IllegalArgumentException("Missing value for [" + criteria.getOperator() + "] criteria on field [" + criteria.getFieldName() + "]");
|
||||
}
|
||||
Serializable value = criteria.getValues().get(0);
|
||||
if(value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!(value instanceof String stringValue))
|
||||
{
|
||||
throw new ClassCastException("Value [" + value + "] for criteria [" + criteria.getFieldName() + "] is not a String, which is required for the [" + criteria.getOperator() + "] operator.");
|
||||
}
|
||||
|
||||
return (stringValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static String getStringFieldValue(Serializable value, String fieldName, QFilterCriteria criterion)
|
||||
{
|
||||
if(value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if(!(value instanceof String stringValue))
|
||||
{
|
||||
throw new ClassCastException("Value [" + value + "] in field [" + fieldName + "] is not a String, which is required for the [" + criterion.getOperator() + "] operator.");
|
||||
}
|
||||
|
||||
return (stringValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Sort list of records based on filter.
|
||||
*******************************************************************************/
|
||||
public static void sortRecordList(QQueryFilter filter, List<QRecord> recordList)
|
||||
{
|
||||
if(filter == null || CollectionUtils.nullSafeIsEmpty(filter.getOrderBys()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
recordList.sort((a, b) ->
|
||||
{
|
||||
for(QFilterOrderBy orderBy : filter.getOrderBys())
|
||||
{
|
||||
Serializable valueA = a.getValue(orderBy.getFieldName());
|
||||
Serializable valueB = b.getValue(orderBy.getFieldName());
|
||||
if(Objects.equals(valueA, valueB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if(isGreaterThan(valueA, valueB) && orderBy.getIsAscending())
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Apply skip & limit attributes from queryInput to a list of records.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> applySkipAndLimit(QueryInput queryInput, List<QRecord> recordList)
|
||||
{
|
||||
Integer skip = queryInput.getSkip();
|
||||
if(skip != null && skip > 0)
|
||||
{
|
||||
if(skip < recordList.size())
|
||||
{
|
||||
recordList = recordList.subList(skip, recordList.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
recordList.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Integer limit = queryInput.getLimit();
|
||||
if(limit != null && limit >= 0 && limit < recordList.size())
|
||||
{
|
||||
recordList = recordList.subList(0, limit);
|
||||
}
|
||||
return recordList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.processes.implementations.basepull;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Class for storing all basepull configuration data
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class BasepullConfiguration implements Serializable
|
||||
{
|
||||
private String tableName; // the table that stores the basepull timestamps
|
||||
private String keyField; // the field in the basepull timestamps table that stores the key of the basepull (e.g., a process name)
|
||||
private String keyValue; // the key applied to the keyField - optional - if not set, process.getName is used.
|
||||
|
||||
private String lastRunTimeFieldName; // the field in the basepull timestamps table that stores the last-run time for the job.
|
||||
private Integer hoursBackForInitialTimestamp; // for the first-run use-case (where there is no row in the timestamps table), how many hours back in time to look.
|
||||
|
||||
private String timestampField; // the name of the field in the table being queried against the last-run timestamp.
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for keyField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getKeyField()
|
||||
{
|
||||
return keyField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for keyField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setKeyField(String keyField)
|
||||
{
|
||||
this.keyField = keyField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for keyField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withKeyField(String keyField)
|
||||
{
|
||||
this.keyField = keyField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for keyValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getKeyValue()
|
||||
{
|
||||
return keyValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for keyValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setKeyValue(String keyValue)
|
||||
{
|
||||
this.keyValue = keyValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for keyValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withKeyValue(String keyValue)
|
||||
{
|
||||
this.keyValue = keyValue;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for lastRunTimeFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLastRunTimeFieldName()
|
||||
{
|
||||
return lastRunTimeFieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for lastRunTimeFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setLastRunTimeFieldName(String lastRunTimeFieldName)
|
||||
{
|
||||
this.lastRunTimeFieldName = lastRunTimeFieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for lastRunTimeFieldName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withLastRunTimeFieldName(String lastRunTimeFieldName)
|
||||
{
|
||||
this.lastRunTimeFieldName = lastRunTimeFieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hoursBackForInitialTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getHoursBackForInitialTimestamp()
|
||||
{
|
||||
return hoursBackForInitialTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hoursBackForInitialTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setHoursBackForInitialTimestamp(Integer hoursBackForInitialTimestamp)
|
||||
{
|
||||
this.hoursBackForInitialTimestamp = hoursBackForInitialTimestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for hoursBackForInitialTimestamp
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withHoursBackForInitialTimestamp(Integer hoursBackForInitialTimestamp)
|
||||
{
|
||||
this.hoursBackForInitialTimestamp = hoursBackForInitialTimestamp;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timestampField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTimestampField()
|
||||
{
|
||||
return timestampField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timestampField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTimestampField(String timestampField)
|
||||
{
|
||||
this.timestampField = timestampField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timestampField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public BasepullConfiguration withTimestampField(String timestampField)
|
||||
{
|
||||
this.timestampField = timestampField;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.processes.implementations.basepull;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
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.tables.query.QCriteriaOperator;
|
||||
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.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Version of ExtractViaQueryStep that knows how to set up a basepull query.
|
||||
*******************************************************************************/
|
||||
public class ExtractViaBasepullQueryStep extends ExtractViaQueryStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected QQueryFilter getQueryFilter(RunBackendStepInput runBackendStepInput) throws QException
|
||||
{
|
||||
//////////////////////////////////////////////////////////////
|
||||
// get input query filter or if not found, create a new one //
|
||||
//////////////////////////////////////////////////////////////
|
||||
QQueryFilter queryFilter = new QQueryFilter();
|
||||
try
|
||||
{
|
||||
queryFilter = super.getQueryFilter(runBackendStepInput);
|
||||
}
|
||||
catch(QException qe)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we catch here, assume that is because there was no default filter, continue on //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// build up a query filter that is against the source table for the given source table timestamp //
|
||||
// field, finding any records that need processed. //
|
||||
// query will be for: timestamp > lastRun AND timestamp <= thisRun. //
|
||||
// then thisRun will be stored, so the next run shouldn't find any records from thisRun. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
queryFilter.addCriteria(new QFilterCriteria()
|
||||
.withFieldName(runBackendStepInput.getValueString(RunProcessAction.BASEPULL_TIMESTAMP_FIELD))
|
||||
.withOperator(QCriteriaOperator.GREATER_THAN)
|
||||
.withValues(List.of(getLastRunTimeString(runBackendStepInput))));
|
||||
|
||||
queryFilter.addCriteria(new QFilterCriteria()
|
||||
.withFieldName(runBackendStepInput.getValueString(RunProcessAction.BASEPULL_TIMESTAMP_FIELD))
|
||||
.withOperator(QCriteriaOperator.LESS_THAN_OR_EQUALS)
|
||||
.withValues(List.of(getThisRunTimeString(runBackendStepInput))));
|
||||
|
||||
queryFilter.addOrderBy(new QFilterOrderBy(runBackendStepInput.getValueString(RunProcessAction.BASEPULL_TIMESTAMP_FIELD)));
|
||||
|
||||
return (queryFilter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String getLastRunTimeString(RunBackendStepInput runBackendStepInput) throws QException
|
||||
{
|
||||
return (runBackendStepInput.getBasepullLastRunTime().toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String getThisRunTimeString(RunBackendStepInput runBackendStepInput) throws QException
|
||||
{
|
||||
return (runBackendStepInput.getValueInstant(RunProcessAction.BASEPULL_THIS_RUNTIME_KEY).toString());
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -49,6 +50,18 @@ public abstract class AbstractExtractStep implements BackendStep
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Allow subclasses to do an action before the run begins.
|
||||
*******************************************************************************/
|
||||
public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
////////////////////////
|
||||
// noop in base class //
|
||||
////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -54,6 +54,20 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
||||
{
|
||||
public static final String FIELD_SOURCE_TABLE = "sourceTable";
|
||||
|
||||
private QQueryFilter queryFilter;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
super.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
queryFilter = getQueryFilter(runBackendStepInput);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -66,7 +80,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
||||
QueryInput queryInput = new QueryInput(runBackendStepInput.getInstance());
|
||||
queryInput.setSession(runBackendStepInput.getSession());
|
||||
queryInput.setTableName(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
||||
queryInput.setFilter(getQueryFilter(runBackendStepInput));
|
||||
queryInput.setFilter(queryFilter);
|
||||
queryInput.setRecordPipe(getRecordPipe());
|
||||
queryInput.setLimit(getLimit());
|
||||
queryInput.setAsyncJobCallback(runBackendStepInput.getAsyncJobCallback());
|
||||
@ -88,7 +102,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
||||
CountInput countInput = new CountInput(runBackendStepInput.getInstance());
|
||||
countInput.setSession(runBackendStepInput.getSession());
|
||||
countInput.setTableName(runBackendStepInput.getValueString(FIELD_SOURCE_TABLE));
|
||||
countInput.setFilter(getQueryFilter(runBackendStepInput));
|
||||
countInput.setFilter(queryFilter);
|
||||
CountOutput countOutput = new CountAction().execute(countInput);
|
||||
return (countOutput.getCount());
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -49,6 +50,9 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
{
|
||||
public static final String FIELD_DESTINATION_TABLE = "destinationTable";
|
||||
|
||||
protected List<QRecord> recordsToInsert = null;
|
||||
protected List<QRecord> recordsToUpdate = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -58,22 +62,20 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
||||
List<QRecord> recordsToInsert = new ArrayList<>();
|
||||
List<QRecord> recordsToUpdate = new ArrayList<>();
|
||||
for(QRecord record : runBackendStepInput.getRecords())
|
||||
{
|
||||
if(record.getValue(tableMetaData.getPrimaryKeyField()) == null)
|
||||
{
|
||||
recordsToInsert.add(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
recordsToUpdate.add(record);
|
||||
}
|
||||
}
|
||||
evaluateRecords(runBackendStepInput);
|
||||
insertAndUpdateRecords(runBackendStepInput, runBackendStepOutput);
|
||||
}
|
||||
|
||||
if(!recordsToInsert.isEmpty())
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void insertAndUpdateRecords(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(recordsToInsert))
|
||||
{
|
||||
InsertInput insertInput = new InsertInput(runBackendStepInput.getInstance());
|
||||
insertInput.setSession(runBackendStepInput.getSession());
|
||||
@ -84,7 +86,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
runBackendStepOutput.getRecords().addAll(insertOutput.getRecords());
|
||||
}
|
||||
|
||||
if(!recordsToUpdate.isEmpty())
|
||||
if(CollectionUtils.nullSafeHasContents(recordsToUpdate))
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput(runBackendStepInput.getInstance());
|
||||
updateInput.setSession(runBackendStepInput.getSession());
|
||||
@ -110,4 +112,27 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
|
||||
|
||||
return (Optional.of(new InsertAction().openTransaction(insertInput)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void evaluateRecords(RunBackendStepInput runBackendStepInput) throws QException
|
||||
{
|
||||
QTableMetaData tableMetaData = runBackendStepInput.getInstance().getTable(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
|
||||
recordsToInsert = new ArrayList<>();
|
||||
recordsToUpdate = new ArrayList<>();
|
||||
for(QRecord record : runBackendStepInput.getRecords())
|
||||
{
|
||||
if(record.getValue(tableMetaData.getPrimaryKeyField()) == null)
|
||||
{
|
||||
recordsToInsert.add(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
recordsToUpdate.add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
@ -63,6 +64,7 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
||||
RecordPipe recordPipe = new RecordPipe();
|
||||
AbstractExtractStep extractStep = getExtractStep(runBackendStepInput);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
extractStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
AbstractTransformStep transformStep = getTransformStep(runBackendStepInput);
|
||||
AbstractLoadStep loadStep = getLoadStep(runBackendStepInput);
|
||||
@ -88,14 +90,26 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
||||
updateRecordsWithDisplayValuesAndPossibleValues(runBackendStepInput, loadedRecordList);
|
||||
runBackendStepOutput.setRecords(loadedRecordList);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the process summary from the ... transform step? the load step? each knows some... todo? //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, transformStep.doGetProcessSummary(runBackendStepOutput, true));
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the process summary from the load step, if it's a summary-provider -- else, use the transform step (which is always a provider) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(loadStep instanceof ProcessSummaryProviderInterface provider)
|
||||
{
|
||||
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, provider.doGetProcessSummary(runBackendStepOutput, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, transformStep.doGetProcessSummary(runBackendStepOutput, true));
|
||||
}
|
||||
|
||||
transformStep.postRun(runBackendStepInput, runBackendStepOutput);
|
||||
loadStep.postRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// set the flag to state that the basepull timestamp should be updated now. //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
runBackendStepOutput.addValue(RunProcessAction.BASEPULL_READY_TO_UPDATE_TIMESTAMP_FIELD, true);
|
||||
|
||||
/////////////////////
|
||||
// commit the work //
|
||||
/////////////////////
|
||||
|
@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
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.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@ -66,6 +67,12 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
return;
|
||||
}
|
||||
|
||||
if(runBackendStepInput.getFrontendStepBehavior() != null && runBackendStepInput.getFrontendStepBehavior().equals(RunProcessInput.FrontendStepBehavior.SKIP))
|
||||
{
|
||||
LOG.info("Skipping preview because frontent behavior is [" + RunProcessInput.FrontendStepBehavior.SKIP + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// if we're running inside an automation, then skip this step. //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
@ -75,13 +82,21 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// request a count from the extract step //
|
||||
///////////////////////////////////////////
|
||||
//////////////////////////////////////////
|
||||
// set up the extract & transform steps //
|
||||
//////////////////////////////////////////
|
||||
AbstractExtractStep extractStep = getExtractStep(runBackendStepInput);
|
||||
Integer recordCount = extractStep.doCount(runBackendStepInput);
|
||||
RecordPipe recordPipe = new RecordPipe();
|
||||
extractStep.setLimit(limit);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
extractStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
Integer recordCount = extractStep.doCount(runBackendStepInput);
|
||||
runBackendStepOutput.addValue(StreamedETLProcess.FIELD_RECORD_COUNT, recordCount);
|
||||
|
||||
AbstractTransformStep transformStep = getTransformStep(runBackendStepInput);
|
||||
transformStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the count is less than the normal limit here, and this process supports validation, then go straight to the validation step //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -93,16 +108,6 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
// return;
|
||||
// }
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// proceed with a doing a limited extract & transform //
|
||||
////////////////////////////////////////////////////////
|
||||
RecordPipe recordPipe = new RecordPipe();
|
||||
extractStep.setLimit(limit);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
|
||||
AbstractTransformStep transformStep = getTransformStep(runBackendStepInput);
|
||||
transformStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
List<QRecord> previewRecordList = new ArrayList<>();
|
||||
new AsyncRecordPipeLoop().run("StreamedETL>Preview>ExtractStep", PROCESS_OUTPUT_RECORD_LIST_LIMIT, recordPipe, (status) ->
|
||||
{
|
||||
@ -140,7 +145,6 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -154,7 +158,7 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// make streamed input & output objects from the run input & outputs //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
StreamedBackendStepInput streamedBackendStepInput = new StreamedBackendStepInput(runBackendStepInput, qRecords);
|
||||
StreamedBackendStepInput streamedBackendStepInput = new StreamedBackendStepInput(runBackendStepInput, qRecords);
|
||||
StreamedBackendStepOutput streamedBackendStepOutput = new StreamedBackendStepOutput(runBackendStepOutput);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
@ -85,6 +85,7 @@ public class StreamedETLValidateStep extends BaseStreamedETLStep implements Back
|
||||
AbstractExtractStep extractStep = getExtractStep(runBackendStepInput);
|
||||
extractStep.setLimit(null);
|
||||
extractStep.setRecordPipe(recordPipe);
|
||||
extractStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
AbstractTransformStep transformStep = getTransformStep(runBackendStepInput);
|
||||
transformStep.preRun(runBackendStepInput, runBackendStepOutput);
|
||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwit
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
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.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.AbstractProcessMetaDataBuilder;
|
||||
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;
|
||||
@ -131,8 +136,8 @@ public class StreamedETLWithFrontendProcess
|
||||
.withField(new QFieldMetaData(FIELD_DESTINATION_TABLE, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DESTINATION_TABLE)))
|
||||
.withField(new QFieldMetaData(FIELD_SUPPORTS_FULL_VALIDATION, QFieldType.BOOLEAN).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_SUPPORTS_FULL_VALIDATION, true)))
|
||||
.withField(new QFieldMetaData(FIELD_DEFAULT_QUERY_FILTER, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DEFAULT_QUERY_FILTER)))
|
||||
.withField(new QFieldMetaData(FIELD_EXTRACT_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(extractStepClass)))
|
||||
.withField(new QFieldMetaData(FIELD_TRANSFORM_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(transformStepClass)))
|
||||
.withField(new QFieldMetaData(FIELD_EXTRACT_CODE, QFieldType.STRING).withDefaultValue(extractStepClass == null ? null : new QCodeReference(extractStepClass)))
|
||||
.withField(new QFieldMetaData(FIELD_TRANSFORM_CODE, QFieldType.STRING).withDefaultValue(transformStepClass == null ? null : new QCodeReference(transformStepClass)))
|
||||
.withField(new QFieldMetaData(FIELD_PREVIEW_MESSAGE, QFieldType.STRING).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_PREVIEW_MESSAGE, DEFAULT_PREVIEW_MESSAGE_FOR_INSERT)))
|
||||
);
|
||||
|
||||
@ -153,7 +158,7 @@ public class StreamedETLWithFrontendProcess
|
||||
.withName(STEP_NAME_EXECUTE)
|
||||
.withCode(new QCodeReference(StreamedETLExecuteStep.class))
|
||||
.withInputData(new QFunctionInputMetaData()
|
||||
.withField(new QFieldMetaData(FIELD_LOAD_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(loadStepClass))))
|
||||
.withField(new QFieldMetaData(FIELD_LOAD_CODE, QFieldType.STRING).withDefaultValue(loadStepClass == null ? null : new QCodeReference(loadStepClass))))
|
||||
.withOutputMetaData(new QFunctionOutputMetaData()
|
||||
.withField(new QFieldMetaData(FIELD_PROCESS_SUMMARY, QFieldType.STRING))
|
||||
);
|
||||
@ -169,4 +174,204 @@ public class StreamedETLWithFrontendProcess
|
||||
.addStep(executeStep)
|
||||
.addStep(resultStep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Builder processMetaDataBuilder()
|
||||
{
|
||||
return (new Builder(defineProcessMetaData(null, null, null, Collections.emptyMap())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class Builder extends AbstractProcessMetaDataBuilder
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder(QProcessMetaData processMetaData)
|
||||
{
|
||||
super(processMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for extractStepClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withExtractStepClass(Class<? extends AbstractExtractStep> extractStepClass)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_EXTRACT_CODE, new QCodeReference(extractStepClass));
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for transformStepClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withTransformStepClass(Class<? extends AbstractTransformStep> transformStepClass)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_TRANSFORM_CODE, new QCodeReference(transformStepClass));
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for loadStepClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withLoadStepClass(Class<? extends AbstractLoadStep> loadStepClass)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_LOAD_CODE, new QCodeReference(loadStepClass));
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withSourceTable(String sourceTable)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_SOURCE_TABLE, sourceTable);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for destinationTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withDestinationTable(String destinationTable)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_DESTINATION_TABLE, destinationTable);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for supportsFullValidation
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withSupportsFullValidation(Boolean supportsFullValidation)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_SUPPORTS_FULL_VALIDATION, supportsFullValidation);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for doFullValidation
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withDoFullValidation(Boolean doFullValidation)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_DO_FULL_VALIDATION, doFullValidation);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for defaultQueryFilter
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withDefaultQueryFilter(QQueryFilter defaultQueryFilter)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_DEFAULT_QUERY_FILTER, defaultQueryFilter);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for previewMessage
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withPreviewMessage(String previewMessage)
|
||||
{
|
||||
setInputFieldDefaultValue(FIELD_PREVIEW_MESSAGE, previewMessage);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withName(String name)
|
||||
{
|
||||
processMetaData.setName(name);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withLabel(String name)
|
||||
{
|
||||
processMetaData.setLabel(name);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withTableName(String tableName)
|
||||
{
|
||||
processMetaData.setTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for icon
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withIcon(QIcon icon)
|
||||
{
|
||||
processMetaData.setIcon(icon);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withReviewStepRecordFields(List<QFieldMetaData> fieldList)
|
||||
{
|
||||
QFrontendStepMetaData reviewStep = processMetaData.getFrontendStep(StreamedETLWithFrontendProcess.STEP_NAME_REVIEW);
|
||||
for(QFieldMetaData fieldMetaData : fieldList)
|
||||
{
|
||||
reviewStep.withRecordListField(fieldMetaData);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.mock;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
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;
|
||||
@ -64,6 +65,7 @@ public class MockBackendStep implements BackendStep
|
||||
runBackendStepOutput.setValues(runBackendStepInput.getValues());
|
||||
runBackendStepOutput.addValue(FIELD_MOCK_VALUE, MOCK_VALUE);
|
||||
runBackendStepOutput.addValue("noOfPeopleGreeted", runBackendStepInput.getRecords().size());
|
||||
runBackendStepOutput.addValue(RunProcessAction.BASEPULL_READY_TO_UPDATE_TIMESTAMP_FIELD, true);
|
||||
|
||||
if("there".equalsIgnoreCase(runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX)))
|
||||
{
|
||||
|
@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -46,6 +47,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
*******************************************************************************/
|
||||
public class ExecuteReportStep implements BackendStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
@ -70,10 +75,9 @@ public class ExecuteReportStep implements BackendStep
|
||||
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmm").withZone(ZoneId.systemDefault());
|
||||
String datePart = formatter.format(Instant.now());
|
||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
|
||||
|
||||
runBackendStepOutput.addValue("downloadFileName", report.getLabel() + " " + datePart + ".xlsx");
|
||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
|
||||
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
@ -82,4 +86,26 @@ public class ExecuteReportStep implements BackendStep
|
||||
throw (new QException("Error running report", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getDownloadFileBaseName(RunBackendStepInput runBackendStepInput, QReportMetaData report)
|
||||
{
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmm").withZone(ZoneId.systemDefault());
|
||||
String datePart = formatter.format(Instant.now());
|
||||
|
||||
String downloadFileBaseName = runBackendStepInput.getValueString("downloadFileBaseName");
|
||||
if(!StringUtils.hasContent(downloadFileBaseName))
|
||||
{
|
||||
downloadFileBaseName = report.getLabel();
|
||||
}
|
||||
|
||||
downloadFileBaseName = downloadFileBaseName.replaceAll("/", "-");
|
||||
|
||||
return (downloadFileBaseName + " - " + datePart);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.processes.implementations.reports;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Version of PrepareReportStep for a report that runs off a single record.
|
||||
*******************************************************************************/
|
||||
public class PrepareReportForRecordStep extends PrepareReportStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
super.run(runBackendStepInput, runBackendStepOutput);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// look for the recordId having been posted to the process - error if not found //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
Serializable recordId = null;
|
||||
if("recordIds".equals(runBackendStepInput.getValueString("recordsParam")))
|
||||
{
|
||||
String recordIdsString = runBackendStepInput.getValueString("recordIds");
|
||||
String[] recordIdsArray = recordIdsString.split(",");
|
||||
if(recordIdsArray.length != 1)
|
||||
{
|
||||
throw (new QUserFacingException("Exactly 1 record must be selected as input to this report."));
|
||||
}
|
||||
|
||||
recordId = recordIdsArray[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new QUserFacingException("No record was selected as input to this report."));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// look for the recordI input field on the process - put the input recordId in that field. //
|
||||
// then remove that input field from the process's inputFieldList //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<QFieldMetaData> inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||
if(CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||
{
|
||||
Iterator<QFieldMetaData> inputFieldListIterator = inputFieldList.iterator();
|
||||
while(inputFieldListIterator.hasNext())
|
||||
{
|
||||
QFieldMetaData fieldMetaData = inputFieldListIterator.next();
|
||||
if(fieldMetaData.getName().equals(RunReportForRecordProcess.FIELD_RECORD_ID))
|
||||
{
|
||||
runBackendStepOutput.addValue(RunReportForRecordProcess.FIELD_RECORD_ID, recordId);
|
||||
inputFieldListIterator.remove();
|
||||
runBackendStepOutput.addValue("inputFieldList", inputFieldList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetInput getInput = new GetInput(runBackendStepInput.getInstance());
|
||||
getInput.setSession(runBackendStepInput.getSession());
|
||||
getInput.setTableName(runBackendStepInput.getTableName());
|
||||
getInput.setPrimaryKey(recordId);
|
||||
getInput.setShouldGenerateDisplayValues(true);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
QRecord record = getOutput.getRecord();
|
||||
if(record == null)
|
||||
{
|
||||
throw (new QUserFacingException("The selected record for the report was not found."));
|
||||
}
|
||||
|
||||
String reportName = runBackendStepInput.getValueString("reportName");
|
||||
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||
// runBackendStepOutput.addValue("downloadFileBaseName", runBackendStepInput.getTable().getLabel() + " " + record.getRecordLabel());
|
||||
runBackendStepOutput.addValue("downloadFileBaseName", report.getLabel() + " - " + record.getRecordLabel());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there are no more input fields, then remove the INPUT step from the process. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||
if(!CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||
{
|
||||
removeInputStepFromProcess(runBackendStepOutput);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,10 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
*******************************************************************************/
|
||||
public class PrepareReportStep implements BackendStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
@ -68,12 +72,22 @@ public class PrepareReportStep implements BackendStep
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////////////////////////////
|
||||
// no input? re-route the process to skip the input screen //
|
||||
//////////////////////////////////////////////////////////////
|
||||
List<String> stepList = new ArrayList<>(runBackendStepOutput.getProcessState().getStepList());
|
||||
stepList.removeIf(s -> s.equals(BasicRunReportProcess.STEP_NAME_INPUT));
|
||||
runBackendStepOutput.getProcessState().setStepList(stepList);
|
||||
removeInputStepFromProcess(runBackendStepOutput);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void removeInputStepFromProcess(RunBackendStepOutput runBackendStepOutput)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////
|
||||
// no input? re-route the process to skip the input screen //
|
||||
//////////////////////////////////////////////////////////////
|
||||
List<String> stepList = new ArrayList<>(runBackendStepOutput.getProcessState().getStepList());
|
||||
stepList.removeIf(s -> s.equals(BasicRunReportProcess.STEP_NAME_INPUT));
|
||||
runBackendStepOutput.getProcessState().setStepList(stepList);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.processes.implementations.reports;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
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.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.AbstractProcessMetaDataBuilder;
|
||||
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.QFunctionInputMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Definition for Basic process to run a report.
|
||||
*******************************************************************************/
|
||||
public class RunReportForRecordProcess
|
||||
{
|
||||
public static final String PROCESS_NAME = "reports.forRecord";
|
||||
|
||||
public static final String STEP_NAME_PREPARE = "prepare";
|
||||
public static final String STEP_NAME_INPUT = "input";
|
||||
public static final String STEP_NAME_EXECUTE = "execute";
|
||||
public static final String STEP_NAME_ACCESS = "accessReport";
|
||||
|
||||
public static final String FIELD_REPORT_NAME = "reportName";
|
||||
public static final String FIELD_RECORD_ID = "recordId";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Builder processMetaDataBuilder()
|
||||
{
|
||||
return (new Builder(defineProcessMetaData()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QProcessMetaData defineProcessMetaData()
|
||||
{
|
||||
QStepMetaData prepareStep = new QBackendStepMetaData()
|
||||
.withName(STEP_NAME_PREPARE)
|
||||
.withCode(new QCodeReference(PrepareReportForRecordStep.class))
|
||||
.withInputData(new QFunctionInputMetaData()
|
||||
.withField(new QFieldMetaData(FIELD_REPORT_NAME, QFieldType.STRING))
|
||||
.withField(new QFieldMetaData(FIELD_RECORD_ID, QFieldType.STRING)));
|
||||
|
||||
QStepMetaData inputStep = new QFrontendStepMetaData()
|
||||
.withName(STEP_NAME_INPUT)
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM));
|
||||
|
||||
QStepMetaData executeStep = new QBackendStepMetaData()
|
||||
.withName(STEP_NAME_EXECUTE)
|
||||
.withCode(new QCodeReference(ExecuteReportStep.class))
|
||||
.withInputData(new QFunctionInputMetaData()
|
||||
.withField(new QFieldMetaData(FIELD_REPORT_NAME, QFieldType.STRING)));
|
||||
|
||||
QStepMetaData accessStep = new QFrontendStepMetaData()
|
||||
.withName(STEP_NAME_ACCESS)
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.DOWNLOAD_FORM));
|
||||
// .withViewField(new QFieldMetaData("outputFile", QFieldType.STRING))
|
||||
// .withViewField(new QFieldMetaData("message", QFieldType.STRING));
|
||||
|
||||
return new QProcessMetaData()
|
||||
.withName(PROCESS_NAME)
|
||||
.addStep(prepareStep)
|
||||
.addStep(inputStep)
|
||||
.addStep(executeStep)
|
||||
.addStep(accessStep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class Builder extends AbstractProcessMetaDataBuilder
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder(QProcessMetaData processMetaData)
|
||||
{
|
||||
super(processMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withProcessName(String name)
|
||||
{
|
||||
processMetaData.setName(name);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withTableName(String tableName)
|
||||
{
|
||||
processMetaData.setTableName(tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for icon
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withIcon(QIcon icon)
|
||||
{
|
||||
processMetaData.setIcon(icon);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for reportName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withReportName(String reportName)
|
||||
{
|
||||
setInputFieldDefaultValue(RunReportForRecordProcess.FIELD_REPORT_NAME, reportName);
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.processes.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
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.utils.ListingHash;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility methods for working with QQQ records and table actions inside user -
|
||||
** defined QQQ processes steps.
|
||||
*******************************************************************************/
|
||||
public class GeneralProcessUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTablePrimaryKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders (with a clientId field), build a map of client.id => client record
|
||||
** via getForeignRecordMap(input, orderList, "clientId", "client", "id")
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, QRecord> getForeignRecordMap(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
{
|
||||
Map<Serializable, QRecord> foreignRecordMap = new HashMap<>();
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(foreignTableName);
|
||||
List<Serializable> foreignIds = new ArrayList<>(sourceRecords.stream().map(r -> r.getValue(sourceTableForeignKeyFieldName)).toList());
|
||||
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(foreignTablePrimaryKeyName, QCriteriaOperator.IN, foreignIds)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
for(QRecord foreignRecord : queryOutput.getRecords())
|
||||
{
|
||||
foreignRecordMap.put(foreignRecord.getValue(foreignTablePrimaryKeyName), foreignRecord);
|
||||
}
|
||||
return foreignRecordMap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTableForeignKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders, build a ListingHash of order.id => List(OrderLine records)
|
||||
** via getForeignRecordListingHashMap(input, orderList, "id", "orderLine", "orderId")
|
||||
*******************************************************************************/
|
||||
public static ListingHash<Serializable, QRecord> getForeignRecordListingHashMap(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTableForeignKeyName) throws QException
|
||||
{
|
||||
ListingHash<Serializable, QRecord> foreignRecordMap = new ListingHash<>();
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(foreignTableName);
|
||||
List<Serializable> foreignIds = new ArrayList<>(sourceRecords.stream().map(r -> r.getValue(sourceTableForeignKeyFieldName)).toList());
|
||||
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(foreignTableForeignKeyName, QCriteriaOperator.IN, foreignIds)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
for(QRecord foreignRecord : queryOutput.getRecords())
|
||||
{
|
||||
foreignRecordMap.add(foreignRecord.getValue(foreignTableForeignKeyName), foreignRecord);
|
||||
}
|
||||
return foreignRecordMap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTablePrimaryKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
** and set those foreign records as a value in the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders (with a clientId field), setValue("client", QRecord(client));
|
||||
** via addForeignRecordsToRecordList(input, orderList, "clientId", "client", "id")
|
||||
*******************************************************************************/
|
||||
public static void addForeignRecordsToRecordList(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTablePrimaryKeyName) throws QException
|
||||
{
|
||||
Map<Serializable, QRecord> foreignRecordMap = getForeignRecordMap(parentActionInput, sourceRecords, sourceTableForeignKeyFieldName, foreignTableName, foreignTablePrimaryKeyName);
|
||||
for(QRecord sourceRecord : sourceRecords)
|
||||
{
|
||||
QRecord foreignRecord = foreignRecordMap.get(sourceRecord.getValue(sourceTableForeignKeyFieldName));
|
||||
sourceRecord.setValue(foreignTableName, foreignRecord);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a list of sourceRecords,
|
||||
** lookup records in the foreignTableName,
|
||||
** that have their foreignTableForeignKeyName in the sourceTableForeignKeyFieldName on the sourceRecords.
|
||||
**
|
||||
** e.g., for a list of orders, setValue("orderLine", List(QRecord(orderLine)))
|
||||
** via addForeignRecordsListToRecordList(input, orderList, "id", "orderLine", "orderId")
|
||||
*******************************************************************************/
|
||||
public static void addForeignRecordsListToRecordList(AbstractActionInput parentActionInput, List<QRecord> sourceRecords, String sourceTableForeignKeyFieldName, String foreignTableName, String foreignTableForeignKeyName) throws QException
|
||||
{
|
||||
ListingHash<Serializable, QRecord> foreignRecordMap = getForeignRecordListingHashMap(parentActionInput, sourceRecords, sourceTableForeignKeyFieldName, foreignTableName, foreignTableForeignKeyName);
|
||||
for(QRecord sourceRecord : sourceRecords)
|
||||
{
|
||||
List<QRecord> foreignRecordList = foreignRecordMap.get(sourceRecord.getValue(sourceTableForeignKeyFieldName));
|
||||
if(foreignRecordList != null)
|
||||
{
|
||||
if(foreignRecordList instanceof Serializable s)
|
||||
{
|
||||
sourceRecord.setValue(foreignTableName, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceRecord.setValue(foreignTableName, new ArrayList<>(foreignRecordList));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Run a query on tableName, for where fieldName equals fieldValue, and return
|
||||
** the list of QRecords.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> getRecordListByField(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(fieldName, QCriteriaOperator.EQUALS, List.of(fieldValue))));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
return (queryOutput.getRecords());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Query to get one record by a unique key value. That field can be the primary
|
||||
** key, or any other field on the table. Note, if multiple rows do match the value,
|
||||
** only 1 (determined in an unspecified way) is returned.
|
||||
*******************************************************************************/
|
||||
public static Optional<QRecord> getRecordById(AbstractActionInput parentActionInput, String tableName, String fieldName, Serializable fieldValue) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(fieldName, QCriteriaOperator.EQUALS, List.of(fieldValue))));
|
||||
queryInput.setLimit(1);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
return (queryOutput.getRecords().stream().findFirst());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table.
|
||||
**
|
||||
** Note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static List<QRecord> loadTable(AbstractActionInput parentActionInput, String tableName) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
return (queryOutput.getRecords());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table, into a map, keyed by the keyFieldName.
|
||||
**
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
**
|
||||
** If multiple values are found for the key, they'll squash each other, and only
|
||||
** one random value will appear.
|
||||
**
|
||||
** Also, note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static Map<Serializable, QRecord> loadTableToMap(AbstractActionInput parentActionInput, String tableName, String keyFieldName) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
List<QRecord> records = queryOutput.getRecords();
|
||||
|
||||
Map<Serializable, QRecord> map = new HashMap<>();
|
||||
for(QRecord record : records)
|
||||
{
|
||||
Serializable value = record.getValue(keyFieldName);
|
||||
if(value != null)
|
||||
{
|
||||
map.put(value, record);
|
||||
}
|
||||
}
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table, into a ListingHash, keyed by the keyFieldName.
|
||||
**
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
**
|
||||
** The ordering of the records is not specified.
|
||||
**
|
||||
** Also, note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static ListingHash<Serializable, QRecord> loadTableToListingHash(AbstractActionInput parentActionInput, String tableName, String keyFieldName) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
List<QRecord> records = queryOutput.getRecords();
|
||||
|
||||
ListingHash<Serializable, QRecord> map = new ListingHash<>();
|
||||
for(QRecord record : records)
|
||||
{
|
||||
Serializable value = record.getValue(keyFieldName);
|
||||
if(value != null)
|
||||
{
|
||||
map.add(value, record);
|
||||
}
|
||||
}
|
||||
return (map);
|
||||
}
|
||||
|
||||
}
|
@ -23,9 +23,10 @@ package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
@ -230,34 +231,86 @@ public class JsonUtils
|
||||
** Convert a json object into a QRecord
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QRecord parseQRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields)
|
||||
public static QRecord parseQRecord(JSONObject jsonObject, Map<String, QFieldMetaData> fields, boolean useBackendFieldNames)
|
||||
{
|
||||
QRecord record = new QRecord();
|
||||
|
||||
FIELDS_LOOP:
|
||||
for(String fieldName : fields.keySet())
|
||||
{
|
||||
QFieldMetaData metaData = fields.get(fieldName);
|
||||
String backendName = metaData.getBackendName() != null ? metaData.getBackendName() : fieldName;
|
||||
switch(metaData.getType())
|
||||
String originalBackendName = null;
|
||||
try
|
||||
{
|
||||
case INTEGER -> record.setValue(fieldName, jsonObject.optInt(backendName));
|
||||
case DECIMAL -> record.setValue(fieldName, jsonObject.optBigDecimal(backendName, null));
|
||||
case BOOLEAN -> record.setValue(fieldName, jsonObject.optBoolean(backendName));
|
||||
case DATE_TIME ->
|
||||
QFieldMetaData metaData = fields.get(fieldName);
|
||||
String backendName = fieldName;
|
||||
if(useBackendFieldNames)
|
||||
{
|
||||
String dateTimeString = jsonObject.optString(backendName);
|
||||
if(StringUtils.hasContent(dateTimeString))
|
||||
backendName = metaData.getBackendName() != null ? metaData.getBackendName() : fieldName;
|
||||
}
|
||||
|
||||
originalBackendName = backendName;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the field backend name has dots in it, interpret that to mean traversal down sub-objects //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
JSONObject jsonObjectToUse = jsonObject;
|
||||
if(backendName.contains("."))
|
||||
{
|
||||
ArrayList<String> levels = new ArrayList<>(List.of(backendName.split("\\.")));
|
||||
backendName = levels.remove(levels.size() - 1);
|
||||
|
||||
for(String level : levels)
|
||||
{
|
||||
try
|
||||
{
|
||||
record.setValue(fieldName, LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
||||
jsonObjectToUse = jsonObjectToUse.optJSONObject(level);
|
||||
if(jsonObjectToUse == null)
|
||||
{
|
||||
continue FIELDS_LOOP;
|
||||
}
|
||||
}
|
||||
catch(DateTimeParseException dtpe1)
|
||||
catch(Exception e)
|
||||
{
|
||||
record.setValue(fieldName, LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME));
|
||||
continue FIELDS_LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> record.setValue(fieldName, jsonObject.optString(backendName));
|
||||
|
||||
if(jsonObjectToUse.isNull(backendName))
|
||||
{
|
||||
record.setValue(fieldName, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(metaData.getType())
|
||||
{
|
||||
case INTEGER -> record.setValue(fieldName, jsonObjectToUse.optInt(backendName));
|
||||
case DECIMAL -> record.setValue(fieldName, jsonObjectToUse.optBigDecimal(backendName, null));
|
||||
case BOOLEAN -> record.setValue(fieldName, jsonObjectToUse.optBoolean(backendName));
|
||||
case DATE_TIME ->
|
||||
{
|
||||
String dateTimeString = jsonObjectToUse.optString(backendName);
|
||||
if(StringUtils.hasContent(dateTimeString))
|
||||
{
|
||||
Instant instant = ValueUtils.getValueAsInstant(dateTimeString);
|
||||
record.setValue(fieldName, instant);
|
||||
}
|
||||
}
|
||||
case DATE ->
|
||||
{
|
||||
String dateString = jsonObjectToUse.optString(backendName);
|
||||
if(StringUtils.hasContent(dateString))
|
||||
{
|
||||
LocalDate localDate = ValueUtils.getValueAsLocalDate(dateString);
|
||||
record.setValue(fieldName, localDate);
|
||||
}
|
||||
}
|
||||
default -> record.setValue(fieldName, jsonObjectToUse.optString(backendName));
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Caught exception parsing field [" + fieldName + "] as [" + originalBackendName + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Calendar;
|
||||
@ -498,6 +499,22 @@ public class ValueUtils
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return LocalDateTime.parse(s, DateTimeFormatter.ISO_ZONED_DATE_TIME).toInstant(ZoneOffset.UTC);
|
||||
}
|
||||
catch(DateTimeParseException e2)
|
||||
{
|
||||
try
|
||||
{
|
||||
return LocalDateTime.parse(s, DateTimeFormatter.ISO_DATE_TIME).toInstant(ZoneOffset.UTC);
|
||||
}
|
||||
catch(Exception e3)
|
||||
{
|
||||
// just throw the original
|
||||
}
|
||||
}
|
||||
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
|
@ -23,19 +23,27 @@ package com.kingsrook.qqq.backend.core.actions.processes;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessState;
|
||||
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.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
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.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
@ -47,6 +55,7 @@ import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackend
|
||||
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -70,6 +79,80 @@ public class RunProcessTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testBasepull() throws QException
|
||||
{
|
||||
TestCallback callback = new TestCallback();
|
||||
RunProcessInput request = new RunProcessInput(TestUtils.defineInstance());
|
||||
request.setSession(TestUtils.getMockSession());
|
||||
request.setProcessName(TestUtils.PROCESS_NAME_BASEPULL);
|
||||
request.setCallback(callback);
|
||||
RunProcessOutput result = new RunProcessAction().execute(request);
|
||||
assertNotNull(result);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the last run time and 'this' run time - because the definition states that if no //
|
||||
// rows found, the last runtime timestamp should be for 24 hours ago //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
Instant lastRunTime = (Instant) result.getValues().get(RunProcessAction.BASEPULL_LAST_RUNTIME_KEY);
|
||||
Instant thisRunTime = (Instant) result.getValues().get(RunProcessAction.BASEPULL_THIS_RUNTIME_KEY);
|
||||
assertTrue(thisRunTime.isAfter(lastRunTime), "new run time should be after last run time.");
|
||||
|
||||
DayOfWeek lastRunTimeDayOfWeek = lastRunTime.atZone(ZoneId.systemDefault()).getDayOfWeek();
|
||||
DayOfWeek thisRunTimeDayOfWeek = thisRunTime.atZone(ZoneId.systemDefault()).getDayOfWeek();
|
||||
thisRunTimeDayOfWeek = thisRunTimeDayOfWeek.minus(1);
|
||||
assertEquals(lastRunTimeDayOfWeek.getValue(), thisRunTimeDayOfWeek.getValue(), "last and this run times should be the same day after subtracting a day");
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// make sure new stamp stored in backend too //
|
||||
///////////////////////////////////////////////
|
||||
assertEquals(thisRunTime, getBasepullLastRunTime(), "last run time should be properly stored in backend");
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// run the process one more time and check values //
|
||||
////////////////////////////////////////////////////
|
||||
result = new RunProcessAction().execute(request);
|
||||
assertNotNull(result);
|
||||
|
||||
////////////////////////////////
|
||||
// this should still be after //
|
||||
////////////////////////////////
|
||||
lastRunTime = (Instant) result.getValues().get(RunProcessAction.BASEPULL_LAST_RUNTIME_KEY);
|
||||
thisRunTime = (Instant) result.getValues().get(RunProcessAction.BASEPULL_THIS_RUNTIME_KEY);
|
||||
assertTrue(thisRunTime.isAfter(lastRunTime), "new run time should be after last run time.");
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// make sure new stamp stored in backend too //
|
||||
///////////////////////////////////////////////
|
||||
assertEquals(thisRunTime, getBasepullLastRunTime(), "last run time should be properly stored in backend");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Instant getBasepullLastRunTime() throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria()
|
||||
.withFieldName(TestUtils.BASEPULL_KEY_FIELD_NAME)
|
||||
.withOperator(QCriteriaOperator.EQUALS)
|
||||
.withValues(List.of(TestUtils.PROCESS_NAME_BASEPULL))));
|
||||
queryInput.setSession(TestUtils.getMockSession());
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_BASEPULL);
|
||||
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertNotNull(queryOutput);
|
||||
assertEquals(1, queryOutput.getRecords().size(), "Should have one record");
|
||||
return (ValueUtils.getValueAsInstant(queryOutput.getRecords().get(0).getValue(TestUtils.BASEPULL_LAST_RUN_TIME_FIELD_NAME)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.NoopCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
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.ExecuteCodeOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ExecuteCodeAction
|
||||
*******************************************************************************/
|
||||
class ExecuteCodeActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new NoopCodeExecutionLogger());
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
assertEquals(16, executeCodeOutput.getOutput());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private ExecuteCodeInput setupInput(QInstance qInstance, Map<String, Serializable> context, QCodeExecutionLoggerInterface executionLogger)
|
||||
{
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(qInstance);
|
||||
executeCodeInput.setSession(new QSession());
|
||||
executeCodeInput.setCodeReference(new QCodeReference(ScriptInJava.class, QCodeUsage.CUSTOMIZER));
|
||||
executeCodeInput.setContext(context);
|
||||
executeCodeInput.setExecutionLogger(executionLogger);
|
||||
return executeCodeInput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testLog4jLogger() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new Log4jCodeExecutionLogger());
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
assertEquals(16, executeCodeOutput.getOutput());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableLogger() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new StoreScriptLogAndScriptLogLineExecutionLogger(1701, 1702));
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
assertEquals(16, executeCodeOutput.getOutput());
|
||||
|
||||
List<QRecord> scriptLogRecords = TestUtils.queryTable(qInstance, "scriptLog");
|
||||
List<QRecord> scriptLogLineRecords = TestUtils.queryTable(qInstance, "scriptLogLine");
|
||||
assertEquals(1, scriptLogRecords.size());
|
||||
assertEquals(1701, scriptLogRecords.get(0).getValueInteger("scriptId"));
|
||||
assertEquals(1702, scriptLogRecords.get(0).getValueInteger("scriptRevisionId"));
|
||||
assertEquals(1, scriptLogLineRecords.size());
|
||||
assertEquals(1, scriptLogLineRecords.get(0).getValueInteger("scriptLogId"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testException()
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of(), new NoopCodeExecutionLogger());
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
assertThrows(QCodeException.class, () ->
|
||||
{
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class ScriptInJava implements Function<Map<String, Object>, Serializable>
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Serializable apply(Map<String, Object> context)
|
||||
{
|
||||
((QCodeExecutionLoggerInterface) context.get("logger")).log("Test a log");
|
||||
|
||||
int x = ValueUtils.getValueAsInteger(context.get("x"));
|
||||
return (x * x);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.RunAssociatedScriptOutput;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
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.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.AssociatedScriptCodeReference;
|
||||
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.AssociatedScript;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RunAssociatedScriptAction
|
||||
*******************************************************************************/
|
||||
class RunAssociatedScriptActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
""");
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ok - since the core module doesn't have the javascript language support module as a dep, this action will fail - but at least we can confirm it fails with this specific exception! //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QException.class)
|
||||
.hasRootCauseInstanceOf(ClassNotFoundException.class)
|
||||
.hasRootCauseMessage("com.kingsrook.qqq.languages.javascript.QJavaScriptExecutor");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QInstance setupInstance() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withField(new QFieldMetaData("testScriptId", QFieldType.INTEGER))
|
||||
.withAssociatedScript(new AssociatedScript()
|
||||
.withScriptTypeId(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(instance, table, List.of(
|
||||
new QRecord().withValue("id", 1),
|
||||
new QRecord().withValue("id", 2)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Test Script Type")
|
||||
));
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRecordNotFound() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(-9999)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The requested record.*was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNoScriptInRecord() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The input record.*does not have a script specified for.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBadScriptIdInRecord() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("testScriptId", -9998)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The script for record .* was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNoCurrentScriptRevisionOnScript() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
""");
|
||||
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
getInput.setPrimaryKey(1);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
Integer scriptId = getOutput.getRecord().getValueInteger("testScriptId");
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", scriptId).withValue("currentScriptRevisionId", null)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The script for record .* does not have a current version.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBadCurrentScriptRevisionOnScript() throws QException
|
||||
{
|
||||
QInstance instance = setupInstance();
|
||||
|
||||
insertScript(instance, 1, """
|
||||
return "Hello";
|
||||
""");
|
||||
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
getInput.setPrimaryKey(1);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
Integer scriptId = getOutput.getRecord().getValueInteger("testScriptId");
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(instance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName("script");
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", scriptId).withValue("currentScriptRevisionId", 9997)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
RunAssociatedScriptInput runAssociatedScriptInput = new RunAssociatedScriptInput(instance);
|
||||
runAssociatedScriptInput.setSession(new QSession());
|
||||
runAssociatedScriptInput.setInputValues(Map.of());
|
||||
runAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
runAssociatedScriptInput.setCodeReference(new AssociatedScriptCodeReference()
|
||||
.withRecordTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withRecordPrimaryKey(1)
|
||||
.withFieldName("testScriptId")
|
||||
);
|
||||
RunAssociatedScriptOutput runAssociatedScriptOutput = new RunAssociatedScriptOutput();
|
||||
|
||||
assertThatThrownBy(() -> new RunAssociatedScriptAction().run(runAssociatedScriptInput, runAssociatedScriptOutput))
|
||||
.isInstanceOf(QNotFoundException.class)
|
||||
.hasMessageMatching("The current revision of the script for record .* was not found.*");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void insertScript(QInstance instance, Serializable recordId, String code) throws QException
|
||||
{
|
||||
StoreAssociatedScriptInput storeAssociatedScriptInput = new StoreAssociatedScriptInput(instance);
|
||||
storeAssociatedScriptInput.setSession(new QSession());
|
||||
storeAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
storeAssociatedScriptInput.setRecordPrimaryKey(recordId);
|
||||
storeAssociatedScriptInput.setCode(code);
|
||||
storeAssociatedScriptInput.setFieldName("testScriptId");
|
||||
StoreAssociatedScriptOutput storeAssociatedScriptOutput = new StoreAssociatedScriptOutput();
|
||||
new StoreAssociatedScriptAction().run(storeAssociatedScriptInput, storeAssociatedScriptOutput);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for StoreAssociatedScriptAction
|
||||
*******************************************************************************/
|
||||
class StoreAssociatedScriptActionTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QTableMetaData table = instance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withField(new QFieldMetaData("testScriptId", QFieldType.INTEGER))
|
||||
.withAssociatedScript(new AssociatedScript()
|
||||
.withScriptTypeId(1)
|
||||
.withFieldName("testScriptId")
|
||||
)
|
||||
.withField(new QFieldMetaData("otherScriptId", QFieldType.INTEGER))
|
||||
.withAssociatedScript(new AssociatedScript()
|
||||
.withScriptTypeId(2)
|
||||
.withFieldName("otherScriptId")
|
||||
);
|
||||
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(instance, table, List.of(
|
||||
new QRecord().withValue("id", 1),
|
||||
new QRecord().withValue("id", 2),
|
||||
new QRecord().withValue("id", 3)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(instance, instance.getTable("scriptType"), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Test Script"),
|
||||
new QRecord().withValue("id", 2).withValue("name", "Other Script")
|
||||
));
|
||||
|
||||
StoreAssociatedScriptInput storeAssociatedScriptInput = new StoreAssociatedScriptInput(instance);
|
||||
storeAssociatedScriptInput.setSession(new QSession());
|
||||
storeAssociatedScriptInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
storeAssociatedScriptInput.setRecordPrimaryKey(1);
|
||||
storeAssociatedScriptInput.setCode("var i = 0;");
|
||||
storeAssociatedScriptInput.setCommitMessage("Test commit");
|
||||
storeAssociatedScriptInput.setFieldName("testScriptId");
|
||||
StoreAssociatedScriptOutput storeAssociatedScriptOutput = new StoreAssociatedScriptOutput();
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// insert 1st version of script for record 1 //
|
||||
///////////////////////////////////////////////
|
||||
new StoreAssociatedScriptAction().run(storeAssociatedScriptInput, storeAssociatedScriptOutput);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "testScriptId", 1);
|
||||
assertValueInField(instance, "script", 1, "currentScriptRevisionId", 1);
|
||||
|
||||
////////////////////////////////////////////
|
||||
// add 2nd version of script for record 1 //
|
||||
////////////////////////////////////////////
|
||||
storeAssociatedScriptInput.setCode("var i = 1;");
|
||||
storeAssociatedScriptInput.setCommitMessage("2nd commit");
|
||||
new StoreAssociatedScriptAction().run(storeAssociatedScriptInput, storeAssociatedScriptOutput);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "testScriptId", 1);
|
||||
assertValueInField(instance, "script", 1, "currentScriptRevisionId", 2);
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// insert 1st version of script for record 3 //
|
||||
///////////////////////////////////////////////
|
||||
storeAssociatedScriptInput.setRecordPrimaryKey(3);
|
||||
storeAssociatedScriptInput.setCode("var i = 2;");
|
||||
storeAssociatedScriptInput.setCommitMessage("First Commit here");
|
||||
new StoreAssociatedScriptAction().run(storeAssociatedScriptInput, storeAssociatedScriptOutput);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 3, "testScriptId", 2);
|
||||
assertValueInField(instance, "script", 2, "currentScriptRevisionId", 3);
|
||||
|
||||
/////////////////////////////////////
|
||||
// make sure no script on record 2 //
|
||||
/////////////////////////////////////
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 2, "testScriptId", null);
|
||||
|
||||
////////////////////////////////////
|
||||
// add another script to record 1 //
|
||||
////////////////////////////////////
|
||||
storeAssociatedScriptInput.setRecordPrimaryKey(1);
|
||||
storeAssociatedScriptInput.setCode("var i = 3;");
|
||||
storeAssociatedScriptInput.setCommitMessage("Other field");
|
||||
storeAssociatedScriptInput.setFieldName("otherScriptId");
|
||||
new StoreAssociatedScriptAction().run(storeAssociatedScriptInput, storeAssociatedScriptOutput);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "testScriptId", 1);
|
||||
assertValueInField(instance, TestUtils.TABLE_NAME_PERSON_MEMORY, 1, "otherScriptId", 3);
|
||||
assertValueInField(instance, "script", 3, "currentScriptRevisionId", 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void assertValueInField(QInstance instance, String tableName, Serializable recordId, String fieldName, Serializable value) throws QException
|
||||
{
|
||||
GetInput getInput = new GetInput(instance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(tableName);
|
||||
getInput.setPrimaryKey(recordId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
fail("Expected value [" + value + "] in field [" + fieldName + "], record [" + tableName + "][" + recordId + "], but the record wasn't found...");
|
||||
}
|
||||
Serializable actual = getOutput.getRecord().getValue(fieldName);
|
||||
assertEquals(value, actual, "Expected value in field [" + fieldName + "], record [" + tableName + "][" + recordId + "]");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for TestScriptAction
|
||||
*******************************************************************************/
|
||||
class TestScriptActionTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@Disabled("Not yet done.")
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
TestScriptInput input = new TestScriptInput(instance);
|
||||
TestScriptOutput output = new TestScriptOutput();
|
||||
new TestScriptAction().run(input, output);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for BuildScriptLogAndScriptLogLineExecutionLogger
|
||||
*******************************************************************************/
|
||||
class BuildScriptLogAndScriptLogLineExecutionLoggerTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(instance);
|
||||
executeCodeInput.setSession(new QSession());
|
||||
executeCodeInput.setInput(Map.of("a", 1));
|
||||
|
||||
BuildScriptLogAndScriptLogLineExecutionLogger logger = new BuildScriptLogAndScriptLogLineExecutionLogger(9999, 8888);
|
||||
logger.acceptExecutionStart(executeCodeInput);
|
||||
logger.acceptLogLine("This is a log");
|
||||
logger.acceptLogLine("This is also a log");
|
||||
logger.acceptExecutionEnd(true);
|
||||
|
||||
QRecord scriptLog = logger.getScriptLog();
|
||||
assertNull(scriptLog.getValueInteger("id"));
|
||||
assertNotNull(scriptLog.getValue("startTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("endTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("runTimeMillis"));
|
||||
assertEquals(9999, scriptLog.getValueInteger("scriptId"));
|
||||
assertEquals(8888, scriptLog.getValueInteger("scriptRevisionId"));
|
||||
assertEquals("{a=1}", scriptLog.getValueString("input"));
|
||||
assertEquals("true", scriptLog.getValueString("output"));
|
||||
assertNull(scriptLog.getValueString("exception"));
|
||||
assertFalse(scriptLog.getValueBoolean("hadError"));
|
||||
|
||||
List<QRecord> scriptLogLineRecords = logger.getScriptLogLines();
|
||||
assertEquals(2, scriptLogLineRecords.size());
|
||||
QRecord scriptLogLine = scriptLogLineRecords.get(0);
|
||||
assertNull(scriptLogLine.getValueInteger("scriptLogId"));
|
||||
assertNotNull(scriptLogLine.getValue("timestamp"));
|
||||
assertEquals("This is a log", scriptLogLine.getValueString("text"));
|
||||
scriptLogLine = scriptLogLineRecords.get(1);
|
||||
assertEquals("This is also a log", scriptLogLine.getValueString("text"));
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for StoreScriptLogAndScriptLogLineExecutionLogger
|
||||
*******************************************************************************/
|
||||
class StoreScriptLogAndScriptLogLineExecutionLoggerTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(instance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
ExecuteCodeInput executeCodeInput = new ExecuteCodeInput(instance);
|
||||
executeCodeInput.setSession(new QSession());
|
||||
executeCodeInput.setInput(Map.of("a", 1));
|
||||
|
||||
StoreScriptLogAndScriptLogLineExecutionLogger logger = new StoreScriptLogAndScriptLogLineExecutionLogger(9999, 8888);
|
||||
logger.acceptExecutionStart(executeCodeInput);
|
||||
logger.acceptLogLine("This is a log");
|
||||
logger.acceptLogLine("This is also a log");
|
||||
logger.acceptExecutionEnd(true);
|
||||
|
||||
List<QRecord> scriptLogRecords = TestUtils.queryTable(instance, "scriptLog");
|
||||
assertEquals(1, scriptLogRecords.size());
|
||||
QRecord scriptLog = scriptLogRecords.get(0);
|
||||
assertNotNull(scriptLog.getValueInteger("id"));
|
||||
assertNotNull(scriptLog.getValue("startTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("endTimestamp"));
|
||||
assertNotNull(scriptLog.getValue("runTimeMillis"));
|
||||
assertEquals(9999, scriptLog.getValueInteger("scriptId"));
|
||||
assertEquals(8888, scriptLog.getValueInteger("scriptRevisionId"));
|
||||
assertEquals("{a=1}", scriptLog.getValueString("input"));
|
||||
assertEquals("true", scriptLog.getValueString("output"));
|
||||
assertNull(scriptLog.getValueString("exception"));
|
||||
assertFalse(scriptLog.getValueBoolean("hadError"));
|
||||
|
||||
List<QRecord> scriptLogLineRecords = TestUtils.queryTable(instance, "scriptLogLine");
|
||||
assertEquals(2, scriptLogLineRecords.size());
|
||||
QRecord scriptLogLine = scriptLogLineRecords.get(0);
|
||||
assertEquals(scriptLog.getValueInteger("id"), scriptLogLine.getValueInteger("scriptLogId"));
|
||||
assertNotNull(scriptLogLine.getValue("timestamp"));
|
||||
assertEquals("This is a log", scriptLogLine.getValueString("text"));
|
||||
scriptLogLine = scriptLogLineRecords.get(1);
|
||||
assertEquals("This is also a log", scriptLogLine.getValueString("text"));
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for GetAction
|
||||
**
|
||||
*******************************************************************************/
|
||||
class GetActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** At the core level, there isn't much that can be asserted, as it uses the
|
||||
** mock implementation - just confirming that all of the "wiring" works.
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test() throws QException
|
||||
{
|
||||
GetInput request = new GetInput(TestUtils.defineInstance());
|
||||
request.setSession(TestUtils.getMockSession());
|
||||
request.setTableName("person");
|
||||
request.setPrimaryKey(1);
|
||||
request.setShouldGenerateDisplayValues(true);
|
||||
request.setShouldTranslatePossibleValues(true);
|
||||
GetOutput result = new GetAction().execute(request);
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getRecord());
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user