Merge pull request #22 from Kingsrook/feature/CTLE-509-support-sending-custom-data-fields-to-deposco

Feature/ctle 509 support sending custom data fields to deposco
This commit is contained in:
2023-06-20 10:37:37 -05:00
committed by GitHub
20 changed files with 1205 additions and 17 deletions

View File

@ -76,6 +76,21 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
/*******************************************************************************
** Execute to insert 1 audit, with a list of detail child records provided as just string messages
*******************************************************************************/
public static void executeWithStringDetails(String tableName, Integer recordId, Map<String, Serializable> securityKeyValues, String message, List<String> detailMessages)
{
List<QRecord> detailRecords = null;
if(CollectionUtils.nullSafeHasContents(detailMessages))
{
detailRecords = detailMessages.stream().map(m -> new QRecord().withValue("message", m)).toList();
}
execute(tableName, recordId, securityKeyValues, message, detailRecords);
}
/*******************************************************************************
** Execute to insert 1 audit, with a list of detail child records
*******************************************************************************/

View File

@ -39,12 +39,15 @@ import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallback;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
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.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.QFilterOrderBy;
@ -61,11 +64,14 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.Automatio
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import org.apache.commons.lang.NotImplementedException;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
@ -270,17 +276,40 @@ public class PollingAutomationPerTableRunner implements Runnable
QueryOutput queryOutput = new QueryAction().execute(queryInput);
for(QRecord record : queryOutput.getRecords())
{
// todo - get filter if there is/was one
TableTrigger tableTrigger = new TableTrigger(record);
try
{
QQueryFilter filter = null;
Integer filterId = tableTrigger.getFilterId();
if(filterId != null)
{
GetInput getInput = new GetInput();
getInput.setTableName(SavedFilter.TABLE_NAME);
getInput.setPrimaryKey(filterId);
GetOutput getOutput = new GetAction().execute(getInput);
if(getOutput.getRecord() != null)
{
SavedFilter savedFilter = new SavedFilter(getOutput.getRecord());
filter = JsonUtils.toObject(savedFilter.getFilterJson(), QQueryFilter.class);
}
}
rs.add(new TableAutomationAction()
.withName("Script:" + record.getValue("scriptId"))
.withFilter(null)
.withName("Script:" + tableTrigger.getScriptId())
.withFilter(filter)
.withTriggerEvent(triggerEvent)
.withPriority(record.getValueInteger("priority"))
.withPriority(tableTrigger.getPriority())
.withCodeReference(new QCodeReference(RunRecordScriptAutomationHandler.class))
.withValues(MapBuilder.of("scriptId", record.getValue("scriptId")))
.withValues(MapBuilder.of("scriptId", tableTrigger.getScriptId()))
.withIncludeRecordAssociations(true)
);
}
catch(Exception e)
{
LOG.error("Error setting up table trigger", e, logPair("tableTriggerId", tableTrigger.getId()));
}
}
}
rs.sort(Comparator.comparing(taa -> Objects.requireNonNullElse(taa.getPriority(), Integer.MAX_VALUE)));

View File

@ -101,6 +101,17 @@ public class ExecuteCodeAction
context.putAll(input.getInput());
}
/////////////////////////////////////////////////////////////////////////////////
// set the qCodeExecutor into any context objects which are QCodeExecutorAware //
/////////////////////////////////////////////////////////////////////////////////
for(Serializable value : context.values())
{
if(value instanceof QCodeExecutorAware qCodeExecutorAware)
{
qCodeExecutorAware.setQCodeExecutor(qCodeExecutor);
}
}
Serializable codeOutput = qCodeExecutor.execute(codeReference, context, executionLogger);
output.setOutput(codeOutput);
executionLogger.acceptExecutionEnd(codeOutput);

View File

@ -41,4 +41,14 @@ public interface QCodeExecutor
*******************************************************************************/
Serializable execute(QCodeReference codeReference, Map<String, Serializable> inputContext, QCodeExecutionLoggerInterface executionLogger) throws QCodeException;
/*******************************************************************************
** Process an object from the script's language/runtime into a (more) native java object.
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
**
*******************************************************************************/
default Object convertObjectToJava(Object object)
{
return (object);
}
}

View File

@ -0,0 +1,36 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.scripts;
/*******************************************************************************
** Interface for classes that can accept a QCodeExecutor object via a setter.
*******************************************************************************/
public interface QCodeExecutorAware
{
/*******************************************************************************
**
*******************************************************************************/
void setQCodeExecutor(QCodeExecutor qCodeExecutor);
}

View File

@ -28,6 +28,7 @@ import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
import com.kingsrook.qqq.backend.core.model.scripts.Script;
@ -50,7 +51,7 @@ public class TableTrigger extends QRecordEntity
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
private String tableName;
@QField(/* todo possibleValueSourceName = */)
@QField(possibleValueSourceName = SavedFilter.TABLE_NAME)
private Integer filterId;
@QField(possibleValueSourceName = Script.TABLE_NAME)

View File

@ -77,6 +77,21 @@ public class QPossibleValueSource implements TopLevelMetaDataInterface
/*******************************************************************************
** Create a new possible value source, for a table, with default settings.
** e.g., name & table name from the tableName parameter; type=TABLE; and LABEL_ONLY format
*******************************************************************************/
public static QPossibleValueSource newForTable(String tableName)
{
return new QPossibleValueSource()
.withName(tableName)
.withType(QPossibleValueSourceType.TABLE)
.withTableName(tableName)
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -0,0 +1,283 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.savedfilters;
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;
/*******************************************************************************
** Entity bean for the saved filter table
*******************************************************************************/
public class SavedFilter extends QRecordEntity
{
public static final String TABLE_NAME = "savedFilter";
@QField(isEditable = false)
private Integer id;
@QField(isEditable = false)
private Instant createDate;
@QField(isEditable = false)
private Instant modifyDate;
@QField(isRequired = true)
private String label;
@QField(isEditable = false)
private String tableName;
@QField(isEditable = false)
private String userId;
@QField(isEditable = false)
private String filterJson;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SavedFilter()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public SavedFilter(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;
}
/*******************************************************************************
** Getter for createDate
**
*******************************************************************************/
public Instant getCreateDate()
{
return createDate;
}
/*******************************************************************************
** Setter for createDate
**
*******************************************************************************/
public void setCreateDate(Instant createDate)
{
this.createDate = createDate;
}
/*******************************************************************************
** Getter for modifyDate
**
*******************************************************************************/
public Instant getModifyDate()
{
return modifyDate;
}
/*******************************************************************************
** Setter for modifyDate
**
*******************************************************************************/
public void setModifyDate(Instant modifyDate)
{
this.modifyDate = modifyDate;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Fluent setter for label
**
*******************************************************************************/
public SavedFilter withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Fluent setter for tableName
**
*******************************************************************************/
public SavedFilter withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Getter for userId
**
*******************************************************************************/
public String getUserId()
{
return userId;
}
/*******************************************************************************
** Setter for userId
**
*******************************************************************************/
public void setUserId(String userId)
{
this.userId = userId;
}
/*******************************************************************************
** Fluent setter for userId
**
*******************************************************************************/
public SavedFilter withUserId(String userId)
{
this.userId = userId;
return (this);
}
/*******************************************************************************
** Getter for filterJson
**
*******************************************************************************/
public String getFilterJson()
{
return filterJson;
}
/*******************************************************************************
** Setter for filterJson
**
*******************************************************************************/
public void setFilterJson(String filterJson)
{
this.filterJson = filterJson;
}
/*******************************************************************************
** Fluent setter for filterJson
**
*******************************************************************************/
public SavedFilter withFilterJson(String filterJson)
{
this.filterJson = filterJson;
return (this);
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.savedfilters;
import java.util.function.Consumer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.DeleteSavedFilterProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.QuerySavedFilterProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.StoreSavedFilterProcess;
/*******************************************************************************
**
*******************************************************************************/
public class SavedFiltersMetaDataProvider
{
/*******************************************************************************
**
*******************************************************************************/
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
instance.addTable(defineSavedFilterTable(backendName, backendDetailEnricher));
instance.addPossibleValueSource(defineSavedFilterPossibleValueSource());
instance.addProcess(QuerySavedFilterProcess.getProcessMetaData());
instance.addProcess(StoreSavedFilterProcess.getProcessMetaData());
instance.addProcess(DeleteSavedFilterProcess.getProcessMetaData());
}
/*******************************************************************************
**
*******************************************************************************/
private QTableMetaData defineSavedFilterTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
{
QTableMetaData table = new QTableMetaData()
.withName(SavedFilter.TABLE_NAME)
.withLabel("Saved Filter")
.withRecordLabelFormat("%s")
.withRecordLabelFields("label")
.withBackendName(backendName)
.withPrimaryKeyField("id")
.withFieldsFromEntity(SavedFilter.class);
if(backendDetailEnricher != null)
{
backendDetailEnricher.accept(table);
}
return (table);
}
/*******************************************************************************
**
*******************************************************************************/
private QPossibleValueSource defineSavedFilterPossibleValueSource()
{
return new QPossibleValueSource()
.withName(SavedFilter.TABLE_NAME)
.withType(QPossibleValueSourceType.TABLE)
.withTableName(SavedFilter.TABLE_NAME)
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY);
}
}

View File

@ -331,7 +331,12 @@ public class ScriptsMetaDataProvider
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
tableMetaData.getField("scriptId").withPossibleValueSourceFilter(new QQueryFilter(
new QFilterCriteria("scriptType.name", QCriteriaOperator.EQUALS, SCRIPT_TYPE_NAME_RECORD)
new QFilterCriteria("scriptType.name", QCriteriaOperator.EQUALS, SCRIPT_TYPE_NAME_RECORD),
new QFilterCriteria("script.tableName", QCriteriaOperator.EQUALS, "${input.tableName}")
));
tableMetaData.getField("filterId").withPossibleValueSourceFilter(new QQueryFilter(
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, "${input.tableName}")
));
return tableMetaData;

View File

@ -0,0 +1,88 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
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.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
/*******************************************************************************
** Process used by the delete filter dialog
*******************************************************************************/
public class DeleteSavedFilterProcess implements BackendStep
{
private static final QLogger LOG = QLogger.getLogger(DeleteSavedFilterProcess.class);
/*******************************************************************************
**
*******************************************************************************/
public static QProcessMetaData getProcessMetaData()
{
return (new QProcessMetaData()
.withName("deleteSavedFilter")
.withStepList(List.of(
new QBackendStepMetaData()
.withCode(new QCodeReference(DeleteSavedFilterProcess.class))
.withName("delete")
)));
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
ActionHelper.validateSession(runBackendStepInput);
try
{
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
DeleteInput input = new DeleteInput();
input.setTableName(SavedFilter.TABLE_NAME);
input.setPrimaryKeys(List.of(savedFilterId));
new DeleteAction().execute(input);
}
catch(Exception e)
{
LOG.warn("Error deleting saved filter", e);
throw (e);
}
}
}

View File

@ -0,0 +1,117 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
import java.io.Serializable;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
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.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.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
/*******************************************************************************
** Process used by the saved filter dialogs
*******************************************************************************/
public class QuerySavedFilterProcess implements BackendStep
{
private static final QLogger LOG = QLogger.getLogger(QuerySavedFilterProcess.class);
/*******************************************************************************
**
*******************************************************************************/
public static QProcessMetaData getProcessMetaData()
{
return (new QProcessMetaData()
.withName("querySavedFilter")
.withStepList(List.of(
new QBackendStepMetaData()
.withCode(new QCodeReference(QuerySavedFilterProcess.class))
.withName("query")
)));
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
ActionHelper.validateSession(runBackendStepInput);
try
{
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
if(savedFilterId != null)
{
GetInput input = new GetInput();
input.setTableName(SavedFilter.TABLE_NAME);
input.setPrimaryKey(savedFilterId);
GetOutput output = new GetAction().execute(input);
runBackendStepOutput.addRecord(output.getRecord());
runBackendStepOutput.addValue("savedFilter", output.getRecord());
runBackendStepOutput.addValue("savedFilterList", (Serializable) List.of(output.getRecord()));
}
else
{
String tableName = runBackendStepInput.getValueString("tableName");
QueryInput input = new QueryInput();
input.setTableName(SavedFilter.TABLE_NAME);
input.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName))
.withOrderBy(new QFilterOrderBy("label")));
QueryOutput output = new QueryAction().execute(input);
runBackendStepOutput.setRecords(output.getRecords());
runBackendStepOutput.addValue("savedFilterList", (Serializable) output.getRecords());
}
}
catch(Exception e)
{
LOG.warn("Error deleting saved filter", e);
throw (e);
}
}
}

View File

@ -0,0 +1,117 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
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.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.model.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
/*******************************************************************************
** Process used by the saved filter dialog
*******************************************************************************/
public class StoreSavedFilterProcess implements BackendStep
{
private static final QLogger LOG = QLogger.getLogger(StoreSavedFilterProcess.class);
/*******************************************************************************
**
*******************************************************************************/
public static QProcessMetaData getProcessMetaData()
{
return (new QProcessMetaData()
.withName("storeSavedFilter")
.withStepList(List.of(
new QBackendStepMetaData()
.withCode(new QCodeReference(StoreSavedFilterProcess.class))
.withName("store")
)));
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
ActionHelper.validateSession(runBackendStepInput);
try
{
QRecord qRecord = new QRecord()
.withValue("id", runBackendStepInput.getValueInteger("id"))
.withValue("label", runBackendStepInput.getValueString("label"))
.withValue("tableName", runBackendStepInput.getValueString("tableName"))
.withValue("filterJson", runBackendStepInput.getValueString("filterJson"))
.withValue("userId", runBackendStepInput.getSession().getUser().getIdReference());
List<QRecord> savedFilterList = new ArrayList<>();
if(qRecord.getValueInteger("id") == null)
{
InsertInput input = new InsertInput();
input.setTableName(SavedFilter.TABLE_NAME);
input.setRecords(List.of(qRecord));
InsertOutput output = new InsertAction().execute(input);
savedFilterList = output.getRecords();
}
else
{
UpdateInput input = new UpdateInput();
input.setTableName(SavedFilter.TABLE_NAME);
input.setRecords(List.of(qRecord));
UpdateOutput output = new UpdateAction().execute(input);
savedFilterList = output.getRecords();
}
runBackendStepOutput.addValue("savedFilterList", (Serializable) savedFilterList);
}
catch(Exception e)
{
LOG.warn("Error storing data saved filter", e);
throw (e);
}
}
}

View File

@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.model.session.QUser;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.AfterEach;
@ -47,7 +48,10 @@ public class BaseTest
@BeforeEach
void baseBeforeEach()
{
QContext.init(TestUtils.defineInstance(), new QSession());
QContext.init(TestUtils.defineInstance(), new QSession()
.withUser(new QUser()
.withIdReference("001")
.withFullName("Anonymous")));
resetMemoryRecordStore();
}

View File

@ -0,0 +1,143 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
import java.util.List;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.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.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFiltersMetaDataProvider;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
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.assertNotNull;
/*******************************************************************************
** Unit test for all saved filter processes
*******************************************************************************/
class SavedFilterProcessTests extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
QInstance qInstance = QContext.getQInstance();
new SavedFiltersMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
{
///////////////////////////////////////////
// query - should be no filters to start //
///////////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("tableName", tableName);
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedFilterList")).size());
}
Integer savedFilterId;
{
////////////////////////
// store a new filter //
////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(StoreSavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("label", "My Filter");
runProcessInput.addValue("tableName", tableName);
runProcessInput.addValue("filterJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
assertEquals(1, savedFilterList.size());
savedFilterId = savedFilterList.get(0).getValueInteger("id");
assertNotNull(savedFilterId);
}
{
////////////////////////////////////
// query - should find our filter //
////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("tableName", tableName);
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
assertEquals(1, savedFilterList.size());
assertEquals(1, savedFilterList.get(0).getValueInteger("id"));
assertEquals("My Filter", savedFilterList.get(0).getValueString("label"));
}
{
///////////////////////
// update our filter //
///////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(StoreSavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("id", savedFilterId);
runProcessInput.addValue("label", "My Updated Filter");
runProcessInput.addValue("tableName", tableName);
runProcessInput.addValue("filterJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
assertEquals(1, savedFilterList.size());
assertEquals(1, savedFilterList.get(0).getValueInteger("id"));
assertEquals("My Updated Filter", savedFilterList.get(0).getValueString("label"));
}
{
///////////////////////
// delete our filter //
///////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(DeleteSavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("id", savedFilterId);
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
}
{
////////////////////////////////////////
// query - should be no filters again //
////////////////////////////////////////
RunProcessInput runProcessInput = new RunProcessInput();
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
runProcessInput.addValue("tableName", tableName);
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedFilterList")).size());
}
}
}

View File

@ -248,7 +248,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
private String writeUpdateSQLPrefix(QTableMetaData table, List<String> fieldsBeingUpdated)
{
String columns = fieldsBeingUpdated.stream()
.map(f -> this.getColumnName(table.getField(f)) + " = ?")
.map(f -> escapeIdentifier(this.getColumnName(table.getField(f))) + " = ?")
.collect(Collectors.joining(", "));
String tableName = escapeIdentifier(getTableName(table));

View File

@ -27,6 +27,10 @@ import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
@ -36,8 +40,10 @@ import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import org.apache.commons.lang.NotImplementedException;
import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.openjdk.nashorn.api.scripting.ScriptObjectMirror;
import org.openjdk.nashorn.internal.runtime.ECMAException;
import org.openjdk.nashorn.internal.runtime.ParserException;
import org.openjdk.nashorn.internal.runtime.Undefined;
/*******************************************************************************
@ -59,6 +65,59 @@ public class QJavaScriptExecutor implements QCodeExecutor
/*******************************************************************************
**
*******************************************************************************/
@Override
public Object convertObjectToJava(Object object)
{
if(object == null || object instanceof String || object instanceof Boolean || object instanceof Integer || object instanceof Long || object instanceof BigDecimal)
{
return (object);
}
else if(object instanceof Float f)
{
return (new BigDecimal(f));
}
else if(object instanceof Double d)
{
return (new BigDecimal(d));
}
else if(object instanceof Undefined)
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// well, we always said we wanted javascript to treat null & undefined the same way... here's our chance //
////////////////////////////////////////////////////////////////////////////////////////////////////////////
return (null);
}
if(object instanceof ScriptObjectMirror scriptObjectMirror)
{
if(scriptObjectMirror.isArray())
{
List<Object> result = new ArrayList<>();
for(String key : scriptObjectMirror.keySet())
{
result.add(Integer.parseInt(key), convertObjectToJava(scriptObjectMirror.get(key)));
}
return (result);
}
else
{
Map<String, Object> result = new HashMap<>();
for(String key : scriptObjectMirror.keySet())
{
result.put(key, convertObjectToJava(scriptObjectMirror.get(key)));
}
return (result);
}
}
return QCodeExecutor.super.convertObjectToJava(object);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -23,7 +23,12 @@ package com.kingsrook.qqq.languages.javascript;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.scripts.ExecuteCodeAction;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutorAware;
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;
@ -31,6 +36,7 @@ import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
@ -241,10 +247,50 @@ class ExecuteCodeActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testConvertObjectToJava() throws QException
{
TestQCodeExecutorAware converter = new TestQCodeExecutorAware();
testOne(1, """
converter.convertObject("one", 1);
converter.convertObject("two", "two");
converter.convertObject("true", true);
converter.convertObject("null", null);
converter.convertObject("undefined", undefined);
converter.convertObject("flatMap", {"a": 1, "b": "c"});
converter.convertObject("flatList", ["a", 1, "b", "c"]);
converter.convertObject("mixedMap", {"a": [1, {"2": "3"}], "b": {"c": ["d"]}});
""", MapBuilder.of("converter", converter));
assertEquals(1, converter.getConvertedObject("one"));
assertEquals("two", converter.getConvertedObject("two"));
assertEquals(true, converter.getConvertedObject("true"));
assertNull(converter.getConvertedObject("null"));
assertNull(converter.getConvertedObject("undefined"));
assertEquals(Map.of("a", 1, "b", "c"), converter.getConvertedObject("flatMap"));
assertEquals(List.of("a", 1, "b", "c"), converter.getConvertedObject("flatList"));
assertEquals(Map.of("a", List.of(1, Map.of("2", "3")), "b", Map.of("c", List.of("d"))), converter.getConvertedObject("mixedMap"));
}
/*******************************************************************************
**
*******************************************************************************/
private OneTestOutput testOne(Integer inputValueC, String code) throws QException
{
return (testOne(inputValueC, code, null));
}
/*******************************************************************************
**
*******************************************************************************/
private OneTestOutput testOne(Integer inputValueC, String code, Map<String, Serializable> additionalContext) throws QException
{
System.out.println();
QInstance instance = TestUtils.defineInstance();
@ -259,6 +305,14 @@ class ExecuteCodeActionTest extends BaseTest
input.withContext("input", testInput);
input.withContext("output", testOutput);
if(additionalContext != null)
{
for(Map.Entry<String, Serializable> entry : additionalContext.entrySet())
{
input.withContext(entry.getKey(), entry.getValue());
}
}
ExecuteCodeOutput output = new ExecuteCodeOutput();
ExecuteCodeAction executeCodeAction = new ExecuteCodeAction();
@ -269,6 +323,49 @@ class ExecuteCodeActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
public static class TestQCodeExecutorAware implements QCodeExecutorAware, Serializable
{
private QCodeExecutor qCodeExecutor;
private Map<String, Object> convertedObjectMap = new HashMap<>();
/*******************************************************************************
**
*******************************************************************************/
@Override
public void setQCodeExecutor(QCodeExecutor qCodeExecutor)
{
this.qCodeExecutor = qCodeExecutor;
}
/*******************************************************************************
**
*******************************************************************************/
public void convertObject(String name, Object inputObject)
{
convertedObjectMap.put(name, qCodeExecutor.convertObjectToJava(inputObject));
}
/*******************************************************************************
**
*******************************************************************************/
public Object getConvertedObject(String name)
{
return (convertedObjectMap.get(name));
}
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -33,9 +33,12 @@ import com.kingsrook.qqq.api.model.APIVersion;
import com.kingsrook.qqq.api.model.actions.HttpApiResponse;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaData;
import com.kingsrook.qqq.api.model.metadata.ApiInstanceMetaDataContainer;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutor;
import com.kingsrook.qqq.backend.core.actions.scripts.QCodeExecutorAware;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
import org.json.JSONObject;
@ -44,11 +47,13 @@ import org.json.JSONObject;
/*******************************************************************************
** Object injected into script context, for interfacing with a QQQ API.
*******************************************************************************/
public class ApiScriptUtils implements Serializable
public class ApiScriptUtils implements QCodeExecutorAware, Serializable
{
private String apiName;
private String apiVersion;
private QCodeExecutor qCodeExecutor;
/*******************************************************************************
@ -169,6 +174,7 @@ public class ApiScriptUtils implements Serializable
public Map<String, Serializable> insert(String tableApiName, Object body) throws QException
{
validateApiNameAndVersion("insert(" + tableApiName + ")");
body = processBodyToJsonString(body);
return (ApiImplementation.insert(getApiInstanceMetaData(), apiVersion, tableApiName, String.valueOf(body)));
}
@ -180,6 +186,7 @@ public class ApiScriptUtils implements Serializable
public List<Map<String, Serializable>> bulkInsert(String tableApiName, Object body) throws QException
{
validateApiNameAndVersion("bulkInsert(" + tableApiName + ")");
body = processBodyToJsonString(body);
return (ApiImplementation.bulkInsert(getApiInstanceMetaData(), apiVersion, tableApiName, String.valueOf(body)));
}
@ -191,17 +198,61 @@ public class ApiScriptUtils implements Serializable
public void update(String tableApiName, Object primaryKey, Object body) throws QException
{
validateApiNameAndVersion("update(" + tableApiName + "," + primaryKey + ")");
body = processBodyToJsonString(body);
ApiImplementation.update(getApiInstanceMetaData(), apiVersion, tableApiName, String.valueOf(primaryKey), String.valueOf(body));
}
/*******************************************************************************
** Take a "body" object, which maybe defined in the script's language/run-time,
** and try to process it into a JSON String (which is what the API Implementation wants)
*******************************************************************************/
private Object processBodyToJsonString(Object body)
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if the caller already supplied the object as a string, then return that string. //
// and in case it can't be parsed as json, well, let that error come out of the api implementation, and go back to the caller. //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(body instanceof String)
{
return (body);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if the input body wasn't a json string, try to convert it from a language-type object (e.g., javscript) to a java-object, //
// then make JSON out of that for the APIImplementation //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Object bodyJavaObject = processInputObjectViaCodeExecutor(body);
return JsonUtils.toJson(bodyJavaObject);
}
/*******************************************************************************
** Use the QCodeExecutor (if we have one) to process an input object from the
** script's language into a (more) native java object.
** e.g., a Nashorn ScriptObjectMirror will end up as a "primitive", or a List or Map of such
*******************************************************************************/
private Object processInputObjectViaCodeExecutor(Object body)
{
if(qCodeExecutor == null || body == null)
{
return (body);
}
return (qCodeExecutor.convertObjectToJava(body));
}
/*******************************************************************************
**
*******************************************************************************/
public List<Map<String, Serializable>> bulkUpdate(String tableApiName, Object body) throws QException
{
validateApiNameAndVersion("bulkUpdate(" + tableApiName + ")");
body = processBodyToJsonString(body);
return (ApiImplementation.bulkUpdate(getApiInstanceMetaData(), apiVersion, tableApiName, String.valueOf(body)));
}
@ -224,6 +275,7 @@ public class ApiScriptUtils implements Serializable
public List<Map<String, Serializable>> bulkDelete(String tableApiName, Object body) throws QException
{
validateApiNameAndVersion("bulkDelete(" + tableApiName + ")");
body = processBodyToJsonString(body);
return (ApiImplementation.bulkDelete(getApiInstanceMetaData(), apiVersion, tableApiName, String.valueOf(body)));
}
@ -308,4 +360,15 @@ public class ApiScriptUtils implements Serializable
}
return paramMap;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public void setQCodeExecutor(QCodeExecutor qCodeExecutor)
{
this.qCodeExecutor = qCodeExecutor;
}
}

View File

@ -66,6 +66,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.ReportType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFiltersMetaDataProvider;
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
@ -156,6 +157,7 @@ public class TestUtils
qInstance.addBackend(defineMemoryBackend());
try
{
new SavedFiltersMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
new ScriptsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
}
catch(Exception e)
@ -362,8 +364,7 @@ public class TestUtils
.withType(QPossibleValueSourceType.TABLE)
.withTableName(TABLE_NAME_PERSON)
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_PARENS_ID)
.withOrderByField("id")
);
.withOrderByField("id"));
}