mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 13:40:44 +00:00
Merge branch 'feature/QQQ-28-bulk-ops-frontend' into feature/sprint-7-integration
This commit is contained in:
@ -52,7 +52,7 @@ public class AsyncJobCallback
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Update the message
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void updateStatus(String message)
|
public void updateStatus(String message)
|
||||||
{
|
{
|
||||||
@ -63,7 +63,20 @@ public class AsyncJobCallback
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Update all 3 status fields
|
||||||
|
*******************************************************************************/
|
||||||
|
public void updateStatus(String message, int current, int total)
|
||||||
|
{
|
||||||
|
this.asyncJobStatus.setMessage(message);
|
||||||
|
this.asyncJobStatus.setCurrent(current);
|
||||||
|
this.asyncJobStatus.setTotal(total);
|
||||||
|
storeUpdatedStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Update the current and total fields (e.g., 1 of 2, 2 of 2, 3 of 2)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void updateStatus(int current, int total)
|
public void updateStatus(int current, int total)
|
||||||
{
|
{
|
||||||
@ -75,13 +88,12 @@ public class AsyncJobCallback
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Remove the values from the current & total fields
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void updateStatus(String message, int current, int total)
|
public void clearCurrentAndTotal()
|
||||||
{
|
{
|
||||||
this.asyncJobStatus.setMessage(message);
|
this.asyncJobStatus.setCurrent(null);
|
||||||
this.asyncJobStatus.setCurrent(current);
|
this.asyncJobStatus.setTotal(null);
|
||||||
this.asyncJobStatus.setTotal(total);
|
|
||||||
storeUpdatedStatus();
|
storeUpdatedStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,4 +37,17 @@ public interface DeleteInterface
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
DeleteOutput execute(DeleteInput deleteInput) throws QException;
|
DeleteOutput execute(DeleteInput deleteInput) throws QException;
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Specify whether this particular module's delete action natively supports
|
||||||
|
** receiving a queryFilter as input (e.g., SQL does). If the module doesn't
|
||||||
|
** support a query filter, then the qqq framework (DeleteAction) will, if it
|
||||||
|
** receives a queryFilter in its input, it will execute the query, and pass
|
||||||
|
** the list of primary keys down into the module's delete implementation.
|
||||||
|
*******************************************************************************/
|
||||||
|
default boolean supportsQueryFilterInput()
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ public class RunProcessAction
|
|||||||
///////////////////////
|
///////////////////////
|
||||||
// Run backend steps //
|
// Run backend steps //
|
||||||
///////////////////////
|
///////////////////////
|
||||||
runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, processState);
|
runBackendStep(runProcessInput, process, runProcessOutput, stateKey, backendStepMetaData, process, processState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -225,11 +225,12 @@ public class RunProcessAction
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Run a single backend step.
|
** Run a single backend step.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, ProcessState processState) throws Exception
|
private void runBackendStep(RunProcessInput runProcessInput, QProcessMetaData process, RunProcessOutput runProcessOutput, UUIDAndTypeStateKey stateKey, QBackendStepMetaData backendStep, QProcessMetaData qProcessMetaData, ProcessState processState) throws Exception
|
||||||
{
|
{
|
||||||
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(runProcessInput.getInstance(), processState);
|
RunBackendStepInput runBackendStepInput = new RunBackendStepInput(runProcessInput.getInstance(), processState);
|
||||||
runBackendStepInput.setProcessName(process.getName());
|
runBackendStepInput.setProcessName(process.getName());
|
||||||
runBackendStepInput.setStepName(backendStep.getName());
|
runBackendStepInput.setStepName(backendStep.getName());
|
||||||
|
runBackendStepInput.setTableName(process.getTableName());
|
||||||
runBackendStepInput.setSession(runProcessInput.getSession());
|
runBackendStepInput.setSession(runProcessInput.getSession());
|
||||||
runBackendStepInput.setCallback(runProcessInput.getCallback());
|
runBackendStepInput.setCallback(runProcessInput.getCallback());
|
||||||
runBackendStepInput.setAsyncJobCallback(runProcessInput.getAsyncJobCallback());
|
runBackendStepInput.setAsyncJobCallback(runProcessInput.getAsyncJobCallback());
|
||||||
|
@ -22,12 +22,21 @@
|
|||||||
package com.kingsrook.qqq.backend.core.actions.tables;
|
package com.kingsrook.qqq.backend.core.actions.tables;
|
||||||
|
|
||||||
|
|
||||||
|
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.ActionHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||||
|
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.modules.backend.QBackendModuleDispatcher;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -36,6 +45,10 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class DeleteAction
|
public class DeleteAction
|
||||||
{
|
{
|
||||||
|
private static final Logger LOG = LogManager.getLogger(DeleteAction.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -46,8 +59,62 @@ public class DeleteAction
|
|||||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(deleteInput.getBackend());
|
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(deleteInput.getBackend());
|
||||||
// todo pre-customization - just get to modify the request?
|
// todo pre-customization - just get to modify the request?
|
||||||
DeleteOutput deleteResult = qModule.getDeleteInterface().execute(deleteInput);
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(deleteInput.getPrimaryKeys()) && deleteInput.getQueryFilter() != null)
|
||||||
|
{
|
||||||
|
throw (new QException("A delete request may not contain both a list of primary keys and a query filter."));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteInterface deleteInterface = qModule.getDeleteInterface();
|
||||||
|
if(deleteInput.getQueryFilter() != null && !deleteInterface.supportsQueryFilterInput())
|
||||||
|
{
|
||||||
|
LOG.info("Querying for primary keys, for backend module " + qModule.getBackendType() + " which does not support queryFilter input for deletes");
|
||||||
|
List<Serializable> primaryKeyList = getPrimaryKeysFromQueryFilter(deleteInput);
|
||||||
|
deleteInput.setPrimaryKeys(primaryKeyList);
|
||||||
|
|
||||||
|
if(primaryKeyList.isEmpty())
|
||||||
|
{
|
||||||
|
LOG.info("0 primaryKeys found. Returning with no-op");
|
||||||
|
DeleteOutput deleteOutput = new DeleteOutput();
|
||||||
|
deleteOutput.setRecordsWithErrors(new ArrayList<>());
|
||||||
|
return (deleteOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteOutput deleteResult = deleteInterface.execute(deleteInput);
|
||||||
// todo post-customization - can do whatever w/ the result if you want
|
// todo post-customization - can do whatever w/ the result if you want
|
||||||
|
|
||||||
return deleteResult;
|
return deleteResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For an implementation that doesn't support a queryFilter as its input,
|
||||||
|
** but a scenario where a query filter was passed in - run the query, to
|
||||||
|
** get a list of primary keys.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static List<Serializable> getPrimaryKeysFromQueryFilter(DeleteInput deleteInput) throws QException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||||
|
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(deleteInput.getBackend());
|
||||||
|
|
||||||
|
QueryInput queryInput = new QueryInput(deleteInput.getInstance(), deleteInput.getSession());
|
||||||
|
queryInput.setTableName(deleteInput.getTableName());
|
||||||
|
queryInput.setFilter(deleteInput.getQueryFilter());
|
||||||
|
QueryOutput queryOutput = qModule.getQueryInterface().execute(queryInput);
|
||||||
|
|
||||||
|
return (queryOutput.getRecords().stream()
|
||||||
|
.map(r -> r.getValue(deleteInput.getTable().getPrimaryKeyField()))
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error getting primary keys from query filter before bulk-delete", e);
|
||||||
|
throw (new QException("Error getting keys from filter prior to delete.", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,31 @@
|
|||||||
package com.kingsrook.qqq.backend.core.instances;
|
package com.kingsrook.qqq.backend.core.instances;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
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.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
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.QFrontendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionOutputMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.delete.BulkDeleteStoreStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditReceiveValuesStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditStoreRecordsStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertReceiveFileStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertStoreRecordsStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.general.LoadInitialRecordsStep;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -48,6 +65,7 @@ public class QInstanceEnricher
|
|||||||
if(qInstance.getTables() != null)
|
if(qInstance.getTables() != null)
|
||||||
{
|
{
|
||||||
qInstance.getTables().values().forEach(this::enrich);
|
qInstance.getTables().values().forEach(this::enrich);
|
||||||
|
defineTableBulkProcesses(qInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(qInstance.getProcesses() != null)
|
if(qInstance.getProcesses() != null)
|
||||||
@ -167,4 +185,229 @@ public class QInstanceEnricher
|
|||||||
return (name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).replaceAll("([A-Z])", " $1"));
|
return (name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).replaceAll("([A-Z])", " $1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Add bulk insert/edit/delete processes to all tables (unless the meta data
|
||||||
|
** already had these processes defined (e.g., the user defined custom ones)
|
||||||
|
*******************************************************************************/
|
||||||
|
private void defineTableBulkProcesses(QInstance qInstance)
|
||||||
|
{
|
||||||
|
for(QTableMetaData table : qInstance.getTables().values())
|
||||||
|
{
|
||||||
|
if(table.getFields() == null)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// these processes can't be defined if there aren't any fields //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - add idea of 'supportsBulkX'
|
||||||
|
String bulkInsertProcessName = table.getName() + ".bulkInsert";
|
||||||
|
if(qInstance.getProcess(bulkInsertProcessName) == null)
|
||||||
|
{
|
||||||
|
defineTableBulkInsert(qInstance, table, bulkInsertProcessName);
|
||||||
|
}
|
||||||
|
|
||||||
|
String bulkEditProcessName = table.getName() + ".bulkEdit";
|
||||||
|
if(qInstance.getProcess(bulkEditProcessName) == null)
|
||||||
|
{
|
||||||
|
defineTableBulkEdit(qInstance, table, bulkEditProcessName);
|
||||||
|
}
|
||||||
|
|
||||||
|
String bulkDeleteProcessName = table.getName() + ".bulkDelete";
|
||||||
|
if(qInstance.getProcess(bulkDeleteProcessName) == null)
|
||||||
|
{
|
||||||
|
defineTableBulkDelete(qInstance, table, bulkDeleteProcessName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void defineTableBulkInsert(QInstance qInstance, QTableMetaData table, String processName)
|
||||||
|
{
|
||||||
|
List<QFieldMetaData> editableFields = table.getFields().values().stream()
|
||||||
|
.filter(QFieldMetaData::getIsEditable)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
String fieldsForHelpText = editableFields.stream()
|
||||||
|
.map(QFieldMetaData::getLabel)
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
|
||||||
|
QFrontendStepMetaData uploadScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("upload")
|
||||||
|
.withLabel("Upload File")
|
||||||
|
.withFormField(new QFieldMetaData("theFile", QFieldType.BLOB).withIsRequired(true))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "Upload a CSV or XLSX file with the following columns: " + fieldsForHelpText));
|
||||||
|
|
||||||
|
QBackendStepMetaData receiveFileStep = new QBackendStepMetaData()
|
||||||
|
.withName("receiveFile")
|
||||||
|
.withCode(new QCodeReference(BulkInsertReceiveFileStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
// todo - our upload file as a field? problem is, its type...
|
||||||
|
.withFieldList(List.of()))
|
||||||
|
.withOutputMetaData(new QFunctionOutputMetaData()
|
||||||
|
.withFieldList(List.of(new QFieldMetaData("noOfFileRows", QFieldType.INTEGER))));
|
||||||
|
|
||||||
|
QFrontendStepMetaData reviewScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("review")
|
||||||
|
.withRecordListFields(editableFields)
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below were parsed from your file, and will be inserted if you click Submit."))
|
||||||
|
.withViewField(new QFieldMetaData("noOfFileRows", QFieldType.INTEGER).withLabel("# of file rows"));
|
||||||
|
|
||||||
|
QBackendStepMetaData storeStep = new QBackendStepMetaData()
|
||||||
|
.withName("storeRecords")
|
||||||
|
.withCode(new QCodeReference(BulkInsertStoreRecordsStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
// todo - our upload file as a field? problem is, its type...
|
||||||
|
.withFieldList(List.of()))
|
||||||
|
.withOutputMetaData(new QFunctionOutputMetaData()
|
||||||
|
.withFieldList(List.of(new QFieldMetaData("noOfFileRows", QFieldType.INTEGER))));
|
||||||
|
|
||||||
|
QFrontendStepMetaData resultsScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("results")
|
||||||
|
.withRecordListFields(new ArrayList<>(table.getFields().values()))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below have been inserted."))
|
||||||
|
.withViewField(new QFieldMetaData("noOfFileRows", QFieldType.INTEGER).withLabel("# of file rows"));
|
||||||
|
|
||||||
|
qInstance.addProcess(
|
||||||
|
new QProcessMetaData()
|
||||||
|
.withName(processName)
|
||||||
|
.withLabel(table.getLabel() + " Bulk Insert")
|
||||||
|
.withTableName(table.getName())
|
||||||
|
.withIsHidden(true)
|
||||||
|
.withStepList(List.of(
|
||||||
|
uploadScreen,
|
||||||
|
receiveFileStep,
|
||||||
|
reviewScreen,
|
||||||
|
storeStep,
|
||||||
|
resultsScreen
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void defineTableBulkEdit(QInstance qInstance, QTableMetaData table, String processName)
|
||||||
|
{
|
||||||
|
List<QFieldMetaData> editableFields = table.getFields().values().stream()
|
||||||
|
.filter(QFieldMetaData::getIsEditable)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
QFrontendStepMetaData editScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("edit")
|
||||||
|
.withLabel("Edit Values")
|
||||||
|
.withFormFields(editableFields)
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", """
|
||||||
|
Flip the switches next to the fields that you want to edit.
|
||||||
|
The values you supply here will be updated in all of the records you are bulk editing.
|
||||||
|
You can clear out the value in a field by flipping the switch on for that field and leaving the input field blank.
|
||||||
|
Fields whose switches are off will not be updated."""))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.BULK_EDIT_FORM)
|
||||||
|
);
|
||||||
|
|
||||||
|
QBackendStepMetaData receiveValuesStep = new QBackendStepMetaData()
|
||||||
|
.withName("receiveValues")
|
||||||
|
.withCode(new QCodeReference(BulkEditReceiveValuesStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
.withRecordListMetaData(new QRecordListMetaData().withTableName(table.getName()))
|
||||||
|
.withField(new QFieldMetaData(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS, QFieldType.STRING))
|
||||||
|
.withFields(editableFields));
|
||||||
|
|
||||||
|
QFrontendStepMetaData reviewScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("review")
|
||||||
|
.withRecordListFields(editableFields)
|
||||||
|
.withViewField(new QFieldMetaData(BulkEditReceiveValuesStep.FIELD_VALUES_BEING_UPDATED, QFieldType.STRING))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below will be updated if you click Submit."));
|
||||||
|
|
||||||
|
QBackendStepMetaData storeStep = new QBackendStepMetaData()
|
||||||
|
.withName("storeRecords")
|
||||||
|
.withCode(new QCodeReference(BulkEditStoreRecordsStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
// todo - our upload file as a field? problem is, its type...
|
||||||
|
.withFieldList(List.of()))
|
||||||
|
.withOutputMetaData(new QFunctionOutputMetaData()
|
||||||
|
.withFieldList(List.of(new QFieldMetaData("noOfFileRows", QFieldType.INTEGER))));
|
||||||
|
|
||||||
|
QFrontendStepMetaData resultsScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("results")
|
||||||
|
.withRecordListFields(new ArrayList<>(table.getFields().values()))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below have been updated."));
|
||||||
|
|
||||||
|
qInstance.addProcess(
|
||||||
|
new QProcessMetaData()
|
||||||
|
.withName(processName)
|
||||||
|
.withLabel(table.getLabel() + " Bulk Edit")
|
||||||
|
.withTableName(table.getName())
|
||||||
|
.withIsHidden(true)
|
||||||
|
.withStepList(List.of(
|
||||||
|
LoadInitialRecordsStep.defineMetaData(table.getName()),
|
||||||
|
editScreen,
|
||||||
|
receiveValuesStep,
|
||||||
|
reviewScreen,
|
||||||
|
storeStep,
|
||||||
|
resultsScreen
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void defineTableBulkDelete(QInstance qInstance, QTableMetaData table, String processName)
|
||||||
|
{
|
||||||
|
QFrontendStepMetaData reviewScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("review")
|
||||||
|
.withRecordListFields(new ArrayList<>(table.getFields().values()))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below will be deleted if you click Submit."));
|
||||||
|
|
||||||
|
QBackendStepMetaData storeStep = new QBackendStepMetaData()
|
||||||
|
.withName("delete")
|
||||||
|
.withCode(new QCodeReference(BulkDeleteStoreStep.class));
|
||||||
|
|
||||||
|
QFrontendStepMetaData resultsScreen = new QFrontendStepMetaData()
|
||||||
|
.withName("results")
|
||||||
|
.withRecordListFields(new ArrayList<>(table.getFields().values()))
|
||||||
|
.withComponent(new QFrontendComponentMetaData()
|
||||||
|
.withType(QComponentType.HELP_TEXT)
|
||||||
|
.withValue("text", "The records below have been deleted."));
|
||||||
|
|
||||||
|
qInstance.addProcess(
|
||||||
|
new QProcessMetaData()
|
||||||
|
.withName(processName)
|
||||||
|
.withLabel(table.getLabel() + " Bulk Delete")
|
||||||
|
.withTableName(table.getName())
|
||||||
|
.withIsHidden(true)
|
||||||
|
.withStepList(List.of(
|
||||||
|
LoadInitialRecordsStep.defineMetaData(table.getName()),
|
||||||
|
reviewScreen,
|
||||||
|
storeStep,
|
||||||
|
resultsScreen
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.actions;
|
package com.kingsrook.qqq.backend.core.model.actions;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobCallback;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobStatus;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
|
||||||
@ -42,6 +45,8 @@ public abstract class AbstractActionInput
|
|||||||
protected QInstance instance;
|
protected QInstance instance;
|
||||||
protected QSession session;
|
protected QSession session;
|
||||||
|
|
||||||
|
private AsyncJobCallback asyncJobCallback;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -59,7 +64,16 @@ public abstract class AbstractActionInput
|
|||||||
public AbstractActionInput(QInstance instance)
|
public AbstractActionInput(QInstance instance)
|
||||||
{
|
{
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
validateInstance(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** performance instance validation (if not previously done).
|
||||||
|
*******************************************************************************/
|
||||||
|
private void validateInstance(QInstance instance)
|
||||||
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// if this instance hasn't been validated yet, do so now //
|
// if this instance hasn't been validated yet, do so now //
|
||||||
// noting that this will also enrich any missing metaData //
|
// noting that this will also enrich any missing metaData //
|
||||||
@ -107,6 +121,7 @@ public abstract class AbstractActionInput
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setInstance(QInstance instance)
|
public void setInstance(QInstance instance)
|
||||||
{
|
{
|
||||||
|
validateInstance(instance);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,4 +146,33 @@ public abstract class AbstractActionInput
|
|||||||
{
|
{
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for asyncJobCallback
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public AsyncJobCallback getAsyncJobCallback()
|
||||||
|
{
|
||||||
|
if(asyncJobCallback == null)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// don't return null here (too easy to NPE). instead, if someone wants one of these, create one and give it to them. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
asyncJobCallback = new AsyncJobCallback(UUID.randomUUID(), new AsyncJobStatus());
|
||||||
|
}
|
||||||
|
return asyncJobCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for asyncJobCallback
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAsyncJobCallback(AsyncJobCallback asyncJobCallback)
|
||||||
|
{
|
||||||
|
this.asyncJobCallback = asyncJobCallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.model.actions.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Model a file that a user uploaded (or otherwise submitted to the qqq backend).
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QUploadedFile implements Serializable
|
||||||
|
{
|
||||||
|
private String filename;
|
||||||
|
private byte[] bytes;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for filename
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getFilename()
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for filename
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFilename(String filename)
|
||||||
|
{
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for bytes
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public byte[] getBytes()
|
||||||
|
{
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for bytes
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setBytes(byte[] bytes)
|
||||||
|
{
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
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.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ public class RunBackendStepInput extends AbstractActionInput
|
|||||||
{
|
{
|
||||||
private ProcessState processState;
|
private ProcessState processState;
|
||||||
private String processName;
|
private String processName;
|
||||||
|
private String tableName;
|
||||||
private String stepName;
|
private String stepName;
|
||||||
private QProcessCallback callback;
|
private QProcessCallback callback;
|
||||||
private AsyncJobCallback asyncJobCallback;
|
private AsyncJobCallback asyncJobCallback;
|
||||||
@ -126,6 +128,55 @@ public class RunBackendStepInput extends AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getTableName()
|
||||||
|
{
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public RunBackendStepInput withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData getTable()
|
||||||
|
{
|
||||||
|
if(tableName == null)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (instance.getTable(tableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for functionName
|
** Getter for functionName
|
||||||
**
|
**
|
||||||
|
@ -27,6 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -183,4 +184,38 @@ public class RunBackendStepOutput extends AbstractActionOutput
|
|||||||
{
|
{
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for a single field's value
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Serializable getValue(String fieldName)
|
||||||
|
{
|
||||||
|
return (processState.getValues().get(fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for a single field's value
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getValueString(String fieldName)
|
||||||
|
{
|
||||||
|
return ((String) getValue(fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for a single field's value
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getValueInteger(String fieldName)
|
||||||
|
{
|
||||||
|
return (ValueUtils.getValueAsInteger(getValue(fieldName)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.delete;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||||
|
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.QInstance;
|
||||||
|
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
public class DeleteInput extends AbstractTableActionInput
|
public class DeleteInput extends AbstractTableActionInput
|
||||||
{
|
{
|
||||||
private List<Serializable> primaryKeys;
|
private List<Serializable> primaryKeys;
|
||||||
|
private QQueryFilter queryFilter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -88,4 +90,37 @@ public class DeleteInput extends AbstractTableActionInput
|
|||||||
this.primaryKeys = primaryKeys;
|
this.primaryKeys = primaryKeys;
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryFilter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQueryFilter getQueryFilter()
|
||||||
|
{
|
||||||
|
return queryFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryFilter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryFilter(QQueryFilter queryFilter)
|
||||||
|
{
|
||||||
|
this.queryFilter = queryFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryFilter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public DeleteInput withQueryFilter(QQueryFilter queryFilter)
|
||||||
|
{
|
||||||
|
this.queryFilter = queryFilter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.actions.tables.delete;
|
package com.kingsrook.qqq.backend.core.model.actions.tables.delete;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -31,18 +33,31 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
|||||||
* Output for a delete action
|
* Output for a delete action
|
||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class DeleteOutput extends AbstractActionOutput
|
public class DeleteOutput extends AbstractActionOutput implements Serializable
|
||||||
{
|
{
|
||||||
private List<QRecord> records;
|
private int deletedRecordCount = 0;
|
||||||
|
private List<QRecord> recordsWithErrors;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
** Getter for deletedRecordCount
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QRecord> getRecords()
|
public int getDeletedRecordCount()
|
||||||
{
|
{
|
||||||
return records;
|
return deletedRecordCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for deletedRecordCount
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setDeletedRecordCount(int deletedRecordCount)
|
||||||
|
{
|
||||||
|
this.deletedRecordCount = deletedRecordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -50,8 +65,40 @@ public class DeleteOutput extends AbstractActionOutput
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setRecords(List<QRecord> records)
|
public List<QRecord> getRecordsWithErrors()
|
||||||
{
|
{
|
||||||
this.records = records;
|
return recordsWithErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setRecordsWithErrors(List<QRecord> recordsWithErrors)
|
||||||
|
{
|
||||||
|
this.recordsWithErrors = recordsWithErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void addRecordWithError(QRecord recordWithError)
|
||||||
|
{
|
||||||
|
if(this.recordsWithErrors == null)
|
||||||
|
{
|
||||||
|
this.recordsWithErrors = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.recordsWithErrors.add(recordWithError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void addToDeletedRecordCount(int i)
|
||||||
|
{
|
||||||
|
deletedRecordCount += i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import java.util.List;
|
|||||||
* A single criteria Component of a Query
|
* A single criteria Component of a Query
|
||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QFilterCriteria
|
public class QFilterCriteria implements Serializable
|
||||||
{
|
{
|
||||||
private String fieldName;
|
private String fieldName;
|
||||||
private QCriteriaOperator operator;
|
private QCriteriaOperator operator;
|
||||||
|
@ -22,11 +22,14 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Bean representing an element of a query order-by clause.
|
** Bean representing an element of a query order-by clause.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QFilterOrderBy
|
public class QFilterOrderBy implements Serializable
|
||||||
{
|
{
|
||||||
private String fieldName;
|
private String fieldName;
|
||||||
private boolean isAscending = true;
|
private boolean isAscending = true;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ import java.util.List;
|
|||||||
* Full "filter" for a query - a list of criteria and order-bys
|
* Full "filter" for a query - a list of criteria and order-bys
|
||||||
*
|
*
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QQueryFilter
|
public class QQueryFilter implements Serializable
|
||||||
{
|
{
|
||||||
private List<QFilterCriteria> criteria = new ArrayList<>();
|
private List<QFilterCriteria> criteria = new ArrayList<>();
|
||||||
private List<QFilterOrderBy> orderBys = new ArrayList<>();
|
private List<QFilterOrderBy> orderBys = new ArrayList<>();
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
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.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -60,6 +61,17 @@ public class QueryInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryInput(QInstance instance, QSession session)
|
||||||
|
{
|
||||||
|
super(instance);
|
||||||
|
setSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for filter
|
** Getter for filter
|
||||||
**
|
**
|
||||||
|
@ -36,6 +36,14 @@ public class UpdateInput extends AbstractTableActionInput
|
|||||||
{
|
{
|
||||||
private List<QRecord> records;
|
private List<QRecord> records;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// allow a caller to specify that they KNOW this optimization (e.g., in SQL) can be made. //
|
||||||
|
// If you set this to true, but it isn't, then you may not get an accurate update. //
|
||||||
|
// If you set this to false, but it isn't, then you may not get the best performance. //
|
||||||
|
// Just leave it null if you don't know what you're dong. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
private Boolean areAllValuesBeingUpdatedTheSame = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -76,4 +84,27 @@ public class UpdateInput extends AbstractTableActionInput
|
|||||||
{
|
{
|
||||||
this.records = records;
|
this.records = records;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for areAllValuesBeingUpdatedTheSame
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Boolean getAreAllValuesBeingUpdatedTheSame()
|
||||||
|
{
|
||||||
|
return areAllValuesBeingUpdatedTheSame;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for areAllValuesBeingUpdatedTheSame
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAreAllValuesBeingUpdatedTheSame(Boolean areAllValuesBeingUpdatedTheSame)
|
||||||
|
{
|
||||||
|
this.areAllValuesBeingUpdatedTheSame = areAllValuesBeingUpdatedTheSame;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,12 @@ package com.kingsrook.qqq.backend.core.model.data;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +57,7 @@ public class QRecord implements Serializable
|
|||||||
private Map<String, Serializable> values = new LinkedHashMap<>();
|
private Map<String, Serializable> values = new LinkedHashMap<>();
|
||||||
private Map<String, String> displayValues = new LinkedHashMap<>();
|
private Map<String, String> displayValues = new LinkedHashMap<>();
|
||||||
private Map<String, Serializable> backendDetails = new LinkedHashMap<>();
|
private Map<String, Serializable> backendDetails = new LinkedHashMap<>();
|
||||||
// todo private List<String> errors = new ArrayList<>();
|
private List<String> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -66,6 +69,16 @@ public class QRecord implements Serializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QRecord(QTableMetaData tableMetaData, Serializable primaryKeyValue)
|
||||||
|
{
|
||||||
|
setTableName(tableMetaData.getName());
|
||||||
|
setValue(tableMetaData.getPrimaryKeyField(), primaryKeyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Copy constructor.
|
** Copy constructor.
|
||||||
@ -77,7 +90,7 @@ public class QRecord implements Serializable
|
|||||||
this.values = record.values;
|
this.values = record.values;
|
||||||
this.displayValues = record.displayValues;
|
this.displayValues = record.displayValues;
|
||||||
this.backendDetails = record.backendDetails;
|
this.backendDetails = record.backendDetails;
|
||||||
// todo! this.errors = record.errors;
|
this.errors = record.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -344,6 +357,49 @@ public class QRecord implements Serializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for errors
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getErrors()
|
||||||
|
{
|
||||||
|
return (errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for errors
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setErrors(List<String> errors)
|
||||||
|
{
|
||||||
|
this.errors = errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Add one error to this record
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void addError(String error)
|
||||||
|
{
|
||||||
|
this.errors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluently Add one error to this record
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QRecord withError(String error)
|
||||||
|
{
|
||||||
|
addError(error);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Convert this record to an QRecordEntity
|
** Convert this record to an QRecordEntity
|
||||||
@ -352,4 +408,5 @@ public class QRecord implements Serializable
|
|||||||
{
|
{
|
||||||
return (QRecordEntity.fromQRecord(c, this));
|
return (QRecordEntity.fromQRecord(c, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,12 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.code;
|
package com.kingsrook.qqq.backend.core.model.metadata.code;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Pointer to code to be ran by the qqq framework, e.g., for custom behavior -
|
||||||
|
** maybe process steps, maybe customization to a table, etc.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QCodeReference
|
public class QCodeReference
|
||||||
{
|
{
|
||||||
@ -33,6 +37,59 @@ public class QCodeReference
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default empty constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes all args
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference(String name, QCodeType codeType, QCodeUsage codeUsage)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.codeType = codeType;
|
||||||
|
this.codeUsage = codeUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that just takes a java class, and infers the other fields.
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference(Class<?> javaClass)
|
||||||
|
{
|
||||||
|
this.name = javaClass.getName();
|
||||||
|
this.codeType = QCodeType.JAVA;
|
||||||
|
|
||||||
|
if(BackendStep.class.isAssignableFrom(javaClass))
|
||||||
|
{
|
||||||
|
this.codeUsage = QCodeUsage.BACKEND_STEP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw (new IllegalStateException("Unable to infer code usage type for class: " + javaClass.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that just takes a java class and code usage.
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference(Class<?> javaClass, QCodeUsage codeUsage)
|
||||||
|
{
|
||||||
|
this.name = javaClass.getName();
|
||||||
|
this.codeType = QCodeType.JAVA;
|
||||||
|
this.codeUsage = codeUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for name
|
** Getter for name
|
||||||
**
|
**
|
||||||
|
@ -40,6 +40,12 @@ public class QFieldMetaData
|
|||||||
private String backendName;
|
private String backendName;
|
||||||
private QFieldType type;
|
private QFieldType type;
|
||||||
private boolean isRequired = false;
|
private boolean isRequired = false;
|
||||||
|
private boolean isEditable = true;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we need "only edit on insert" or "only edit on update" in the future, //
|
||||||
|
// propose doing that in a secondary field, e.g., "onlyEditableOn=insert|update" //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Serializable defaultValue;
|
private Serializable defaultValue;
|
||||||
private String possibleValueSourceName;
|
private String possibleValueSourceName;
|
||||||
@ -315,4 +321,37 @@ public class QFieldMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isEditable
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getIsEditable()
|
||||||
|
{
|
||||||
|
return isEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for isEditable
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIsEditable(boolean isEditable)
|
||||||
|
{
|
||||||
|
this.isEditable = isEditable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFieldMetaData withIsEditable(boolean isEditable)
|
||||||
|
{
|
||||||
|
this.isEditable = isEditable;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,8 @@ public enum QFieldType
|
|||||||
DATE_TIME,
|
DATE_TIME,
|
||||||
TEXT,
|
TEXT,
|
||||||
HTML,
|
HTML,
|
||||||
PASSWORD;
|
PASSWORD,
|
||||||
|
BLOB;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ public class QFrontendFieldMetaData
|
|||||||
private String label;
|
private String label;
|
||||||
private QFieldType type;
|
private QFieldType type;
|
||||||
private boolean isRequired;
|
private boolean isRequired;
|
||||||
|
private boolean isEditable;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// do not add setters. take values from the source-object in the constructor!! //
|
// do not add setters. take values from the source-object in the constructor!! //
|
||||||
@ -48,7 +49,7 @@ public class QFrontendFieldMetaData
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Constructor
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QFrontendFieldMetaData(QFieldMetaData fieldMetaData)
|
public QFrontendFieldMetaData(QFieldMetaData fieldMetaData)
|
||||||
{
|
{
|
||||||
@ -56,6 +57,7 @@ public class QFrontendFieldMetaData
|
|||||||
this.label = fieldMetaData.getLabel();
|
this.label = fieldMetaData.getLabel();
|
||||||
this.type = fieldMetaData.getType();
|
this.type = fieldMetaData.getType();
|
||||||
this.isRequired = fieldMetaData.getIsRequired();
|
this.isRequired = fieldMetaData.getIsRequired();
|
||||||
|
this.isEditable = fieldMetaData.getIsEditable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,4 +103,16 @@ public class QFrontendFieldMetaData
|
|||||||
{
|
{
|
||||||
return isRequired;
|
return isRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for isEditable
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getIsEditable()
|
||||||
|
{
|
||||||
|
return isEditable;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Types of UI Components that can be specified in frontend process steps.
|
||||||
|
*******************************************************************************/
|
||||||
|
public enum QComponentType
|
||||||
|
{
|
||||||
|
HELP_TEXT,
|
||||||
|
BULK_EDIT_FORM;
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// keep these values in sync with QComponentType.ts in qqq-frontend-core //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Definition of a UI component in a frontend process steps.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QFrontendComponentMetaData
|
||||||
|
{
|
||||||
|
private QComponentType type;
|
||||||
|
|
||||||
|
private Map<String, Serializable> values;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for type
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QComponentType getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for type
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setType(QComponentType type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for type
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFrontendComponentMetaData withType(QComponentType type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for values
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Map<String, Serializable> getValues()
|
||||||
|
{
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for values
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValues(Map<String, Serializable> values)
|
||||||
|
{
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for values
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFrontendComponentMetaData withValues(Map<String, Serializable> values)
|
||||||
|
{
|
||||||
|
this.values = values;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for values
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFrontendComponentMetaData withValue(String key, Serializable value)
|
||||||
|
{
|
||||||
|
if(values == null)
|
||||||
|
{
|
||||||
|
values = new HashMap<>();
|
||||||
|
}
|
||||||
|
values.put(key, value);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QFrontendStepMetaData extends QStepMetaData
|
public class QFrontendStepMetaData extends QStepMetaData
|
||||||
{
|
{
|
||||||
|
private List<QFrontendComponentMetaData> components;
|
||||||
private List<QFieldMetaData> formFields;
|
private List<QFieldMetaData> formFields;
|
||||||
private List<QFieldMetaData> viewFields;
|
private List<QFieldMetaData> viewFields;
|
||||||
private List<QFieldMetaData> recordListFields;
|
private List<QFieldMetaData> recordListFields;
|
||||||
@ -50,6 +51,56 @@ public class QFrontendStepMetaData extends QStepMetaData
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for components
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QFrontendComponentMetaData> getComponents()
|
||||||
|
{
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for components
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setComponents(List<QFrontendComponentMetaData> components)
|
||||||
|
{
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for adding 1 component
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFrontendStepMetaData withComponent(QFrontendComponentMetaData component)
|
||||||
|
{
|
||||||
|
if(this.components == null)
|
||||||
|
{
|
||||||
|
this.components = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.components.add(component);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for components
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFrontendStepMetaData withComponents(List<QFrontendComponentMetaData> components)
|
||||||
|
{
|
||||||
|
this.components = components;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for formFields
|
** Getter for formFields
|
||||||
**
|
**
|
||||||
|
@ -124,7 +124,23 @@ public class QFunctionInputMetaData
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for fieldList
|
** Fluently ADD a list of fields to this object's existing list
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFunctionInputMetaData withFields(List<QFieldMetaData> fieldList)
|
||||||
|
{
|
||||||
|
if(this.fieldList == null)
|
||||||
|
{
|
||||||
|
this.fieldList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.fieldList.addAll(fieldList);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent Setter for fieldList - e.g., will overwrite any previously set fields!!
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QFunctionInputMetaData withFieldList(List<QFieldMetaData> fieldList)
|
public QFunctionInputMetaData withFieldList(List<QFieldMetaData> fieldList)
|
||||||
@ -136,10 +152,10 @@ public class QFunctionInputMetaData
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for fieldList
|
** Fluently add a field to the list
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QFunctionInputMetaData addField(QFieldMetaData field)
|
public QFunctionInputMetaData withField(QFieldMetaData field)
|
||||||
{
|
{
|
||||||
if(this.fieldList == null)
|
if(this.fieldList == null)
|
||||||
{
|
{
|
||||||
@ -149,4 +165,17 @@ public class QFunctionInputMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Use withField instead, please.
|
||||||
|
**
|
||||||
|
** @deprecated
|
||||||
|
*******************************************************************************/
|
||||||
|
@Deprecated
|
||||||
|
public QFunctionInputMetaData addField(QFieldMetaData field)
|
||||||
|
{
|
||||||
|
return (withField(field));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public class QRecordListMetaData
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QRecordListMetaData addField(QFieldMetaData field)
|
public QRecordListMetaData withField(QFieldMetaData field)
|
||||||
{
|
{
|
||||||
if(this.fields == null)
|
if(this.fields == null)
|
||||||
{
|
{
|
||||||
@ -126,4 +126,15 @@ public class QRecordListMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Deprecated
|
||||||
|
public QRecordListMetaData addField(QFieldMetaData field)
|
||||||
|
{
|
||||||
|
return (withField(field));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -45,11 +44,7 @@ public class MockDeleteAction implements DeleteInterface
|
|||||||
{
|
{
|
||||||
DeleteOutput rs = new DeleteOutput();
|
DeleteOutput rs = new DeleteOutput();
|
||||||
|
|
||||||
rs.setRecords(deleteInput.getPrimaryKeys().stream().map(primaryKey ->
|
rs.setDeletedRecordCount(deleteInput.getPrimaryKeys().size());
|
||||||
new QRecord()
|
|
||||||
.withTableName(deleteInput.getTableName())
|
|
||||||
.withValue("id", primaryKey))
|
|
||||||
.toList());
|
|
||||||
|
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
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.insert.InsertOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -46,6 +47,16 @@ public class MockInsertAction implements InsertInterface
|
|||||||
|
|
||||||
rs.setRecords(insertInput.getRecords());
|
rs.setRecords(insertInput.getRecords());
|
||||||
|
|
||||||
|
String primaryKeyField = insertInput.getTable().getPrimaryKeyField();
|
||||||
|
int i = 1;
|
||||||
|
for(QRecord record : rs.getRecords())
|
||||||
|
{
|
||||||
|
if(record.getValue(primaryKeyField) == null)
|
||||||
|
{
|
||||||
|
record.setValue(primaryKeyField, i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.delete;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
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.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.actions.tables.delete.DeleteOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Backend step to do a bulk delete.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkDeleteStoreStep implements BackendStep
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
runBackendStepInput.getAsyncJobCallback().updateStatus("Deleting records in database...");
|
||||||
|
runBackendStepInput.getAsyncJobCallback().clearCurrentAndTotal();
|
||||||
|
|
||||||
|
DeleteInput deleteInput = new DeleteInput(runBackendStepInput.getInstance());
|
||||||
|
deleteInput.setSession(runBackendStepInput.getSession());
|
||||||
|
deleteInput.setAsyncJobCallback(runBackendStepInput.getAsyncJobCallback());
|
||||||
|
deleteInput.setTableName(runBackendStepInput.getTableName());
|
||||||
|
|
||||||
|
String queryFilterJSON = runBackendStepInput.getValueString("queryFilterJSON");
|
||||||
|
if(StringUtils.hasContent(queryFilterJSON))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
deleteInput.setQueryFilter(JsonUtils.toObject(queryFilterJSON, QQueryFilter.class));
|
||||||
|
}
|
||||||
|
catch(IOException e)
|
||||||
|
{
|
||||||
|
throw (new QException("Error loading record query filter from process", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(CollectionUtils.nullSafeHasContents(runBackendStepInput.getRecords()))
|
||||||
|
{
|
||||||
|
String primaryKeyField = runBackendStepInput.getTable().getPrimaryKeyField();
|
||||||
|
List<Serializable> primaryKeyList = runBackendStepInput.getRecords().stream()
|
||||||
|
.map(r -> r.getValue(primaryKeyField))
|
||||||
|
.toList();
|
||||||
|
deleteInput.setPrimaryKeys(primaryKeyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteAction deleteAction = new DeleteAction();
|
||||||
|
DeleteOutput deleteOutput = deleteAction.execute(deleteInput);
|
||||||
|
|
||||||
|
// todo - something with the output!!
|
||||||
|
deleteOutput.getRecordsWithErrors();
|
||||||
|
|
||||||
|
runBackendStepOutput.setRecords(runBackendStepInput.getRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.processes.implementations.bulk.edit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Backend step to receive values for a bulk edit.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkEditReceiveValuesStep implements BackendStep
|
||||||
|
{
|
||||||
|
public static final String FIELD_ENABLED_FIELDS = "bulkEditEnabledFields";
|
||||||
|
public static final String FIELD_VALUES_BEING_UPDATED = "valuesBeingUpdated";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
String enabledFieldsString = runBackendStepInput.getValueString(FIELD_ENABLED_FIELDS);
|
||||||
|
String[] enabledFields = enabledFieldsString.split(",");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// put the value in all the records (note, this is just for display on the review screen, //
|
||||||
|
// and/or if we wanted to do some validation - this is NOT what will be store, as the //
|
||||||
|
// Update action only wants fields that are being changed. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
for(QRecord record : runBackendStepInput.getRecords())
|
||||||
|
{
|
||||||
|
for(String fieldName : enabledFields)
|
||||||
|
{
|
||||||
|
Serializable value = runBackendStepInput.getValue(fieldName);
|
||||||
|
record.setValue(fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
// build the string to show the user what fields are being changed //
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
List<String> valuesBeingUpdated = new ArrayList<>();
|
||||||
|
QTableMetaData table = runBackendStepInput.getTable();
|
||||||
|
for(String fieldName : enabledFields)
|
||||||
|
{
|
||||||
|
String label = table.getField(fieldName).getLabel();
|
||||||
|
Serializable value = runBackendStepInput.getValue(fieldName);
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(ValueUtils.getValueAsString(value)))
|
||||||
|
{
|
||||||
|
valuesBeingUpdated.add(label + " will be set to: " + value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valuesBeingUpdated.add(label + " will be cleared out.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runBackendStepOutput.addValue(FIELD_VALUES_BEING_UPDATED, String.join("\n", valuesBeingUpdated));
|
||||||
|
|
||||||
|
runBackendStepOutput.setRecords(runBackendStepInput.getRecords());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.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;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Backend step to store the records from a bulk insert file
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkEditStoreRecordsStep implements BackendStep
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
String enabledFieldsString = runBackendStepInput.getValueString(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS);
|
||||||
|
String[] enabledFields = enabledFieldsString.split(",");
|
||||||
|
QTableMetaData table = runBackendStepInput.getTable();
|
||||||
|
List<QRecord> recordsToUpdate = new ArrayList<>();
|
||||||
|
|
||||||
|
runBackendStepInput.getAsyncJobCallback().updateStatus("Updating values in records...");
|
||||||
|
int i = 1;
|
||||||
|
for(QRecord record : runBackendStepInput.getRecords())
|
||||||
|
{
|
||||||
|
runBackendStepInput.getAsyncJobCallback().updateStatus(i++, runBackendStepInput.getRecords().size());
|
||||||
|
QRecord recordToUpdate = new QRecord();
|
||||||
|
recordsToUpdate.add(recordToUpdate);
|
||||||
|
|
||||||
|
recordToUpdate.setValue(table.getPrimaryKeyField(), record.getValue(table.getPrimaryKeyField()));
|
||||||
|
for(String fieldName : enabledFields)
|
||||||
|
{
|
||||||
|
Serializable value = runBackendStepInput.getValue(fieldName);
|
||||||
|
recordToUpdate.setValue(fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runBackendStepInput.getAsyncJobCallback().updateStatus("Updating database...");
|
||||||
|
runBackendStepInput.getAsyncJobCallback().clearCurrentAndTotal();
|
||||||
|
|
||||||
|
UpdateInput updateInput = new UpdateInput(runBackendStepInput.getInstance());
|
||||||
|
updateInput.setSession(runBackendStepInput.getSession());
|
||||||
|
updateInput.setAsyncJobCallback(runBackendStepInput.getAsyncJobCallback());
|
||||||
|
updateInput.setAreAllValuesBeingUpdatedTheSame(true);
|
||||||
|
updateInput.setTableName(runBackendStepInput.getTableName());
|
||||||
|
updateInput.setRecords(recordsToUpdate);
|
||||||
|
|
||||||
|
UpdateAction updateAction = new UpdateAction();
|
||||||
|
UpdateOutput updateOutput = updateAction.execute(updateInput);
|
||||||
|
|
||||||
|
runBackendStepOutput.setRecords(updateOutput.getRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.insert;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Backend step to receive a bulk-insert upload file
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkInsertReceiveFileStep implements BackendStep
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
List<QRecord> qRecords = BulkInsertUtils.getQRecordsFromFile(runBackendStepInput);
|
||||||
|
|
||||||
|
runBackendStepOutput.addValue("noOfFileRows", qRecords.size());
|
||||||
|
runBackendStepOutput.setRecords(qRecords);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
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.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Backend step to store the records from a bulk insert file
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkInsertStoreRecordsStep implements BackendStep
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
List<QRecord> qRecords = BulkInsertUtils.getQRecordsFromFile(runBackendStepInput);
|
||||||
|
|
||||||
|
InsertInput insertInput = new InsertInput(runBackendStepInput.getInstance());
|
||||||
|
insertInput.setSession(runBackendStepInput.getSession());
|
||||||
|
insertInput.setTableName(runBackendStepInput.getTableName());
|
||||||
|
insertInput.setRecords(qRecords);
|
||||||
|
|
||||||
|
InsertAction insertAction = new InsertAction();
|
||||||
|
InsertOutput insertOutput = insertAction.execute(insertInput);
|
||||||
|
|
||||||
|
runBackendStepOutput.setRecords(insertOutput.getRecords());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.insert;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import com.kingsrook.qqq.backend.core.adapters.CsvToQRecordAdapter;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.QKeyBasedFieldMapping;
|
||||||
|
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.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.AbstractStateKey;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.TempFileStateProvider;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Utility methods used by bulk insert steps
|
||||||
|
*******************************************************************************/
|
||||||
|
public class BulkInsertUtils
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
static List<QRecord> getQRecordsFromFile(RunBackendStepInput runBackendStepInput) throws QException
|
||||||
|
{
|
||||||
|
AbstractStateKey stateKey = (AbstractStateKey) runBackendStepInput.getValue("uploadedFileKey");
|
||||||
|
Optional<QUploadedFile> optionalUploadedFile = TempFileStateProvider.getInstance().get(QUploadedFile.class, stateKey);
|
||||||
|
if(optionalUploadedFile.isEmpty())
|
||||||
|
{
|
||||||
|
throw (new QException("Could not find uploaded file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = optionalUploadedFile.get().getBytes();
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// let the user specify field labels instead names //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
QTableMetaData table = runBackendStepInput.getTable();
|
||||||
|
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping();
|
||||||
|
for(Map.Entry<String, QFieldMetaData> entry : table.getFields().entrySet())
|
||||||
|
{
|
||||||
|
mapping.addMapping(entry.getKey(), entry.getValue().getLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - sniff out file type...
|
||||||
|
String tableName = runBackendStepInput.getTableName();
|
||||||
|
List<QRecord> qRecords = new CsvToQRecordAdapter().buildRecordsFromCsv(new String(bytes), runBackendStepInput.getInstance().getTable(tableName), mapping);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// remove values from any non-editable fields //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
List<QFieldMetaData> nonEditableFields = table.getFields().values().stream()
|
||||||
|
.filter(f -> !f.getIsEditable())
|
||||||
|
.toList();
|
||||||
|
if(!nonEditableFields.isEmpty())
|
||||||
|
{
|
||||||
|
for(QRecord qRecord : qRecords)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData nonEditableField : nonEditableFields)
|
||||||
|
{
|
||||||
|
qRecord.setValue(nonEditableField.getName(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (qRecords);
|
||||||
|
}
|
||||||
|
}
|
@ -26,12 +26,14 @@ import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
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.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
|
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.code.QCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -49,8 +51,12 @@ public class LoadInitialRecordsStep implements BackendStep
|
|||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// actually, this is a no-op... we Just need a backendStep to be the first step in the process //
|
// basically this is a no-op... we Just need a backendStep to be the first step in the process //
|
||||||
|
// but, while we're here, go ahead and put the query filter in the payload as a value, in case //
|
||||||
|
// someone else wants it (see BulkDelete) //
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QQueryFilter queryFilter = runBackendStepInput.getCallback().getQueryFilter();
|
||||||
|
runBackendStepOutput.addValue("queryFilterJSON", JsonUtils.toJson(queryFilter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,20 +49,22 @@ public class MockBackendStep implements BackendStep
|
|||||||
@Override
|
@Override
|
||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
runBackendStepOutput.getRecords().forEach(r ->
|
|
||||||
{
|
|
||||||
r.setValue(FIELD_MOCK_VALUE, "Ha ha!");
|
|
||||||
LOG.info("We are mocking {}: {}", r.getValueString("firstName"), r.getValue(FIELD_MOCK_VALUE));
|
|
||||||
});
|
|
||||||
|
|
||||||
runBackendStepOutput.setValues(runBackendStepInput.getValues());
|
|
||||||
runBackendStepOutput.addValue(FIELD_MOCK_VALUE, MOCK_VALUE);
|
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
// mock the "greet" process... //
|
// mock the "greet" process... //
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
runBackendStepOutput.addValue("outputMessage", runBackendStepInput.getValueString(FIELD_GREETING_PREFIX) + " X " + runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX));
|
runBackendStepOutput.addValue("outputMessage", runBackendStepInput.getValueString(FIELD_GREETING_PREFIX) + " X " + runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX));
|
||||||
|
|
||||||
|
runBackendStepInput.getRecords().forEach(r ->
|
||||||
|
{
|
||||||
|
LOG.info("We are mocking {}: {}", r.getValueString("firstName"), r.getValue(FIELD_MOCK_VALUE));
|
||||||
|
r.setValue(FIELD_MOCK_VALUE, "Ha ha!");
|
||||||
|
r.setValue("greetingMessage", runBackendStepInput.getValueString(FIELD_GREETING_PREFIX) + " " + r.getValueString("firstName") + " " + runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX));
|
||||||
|
});
|
||||||
|
|
||||||
|
runBackendStepOutput.setValues(runBackendStepInput.getValues());
|
||||||
|
runBackendStepOutput.addValue(FIELD_MOCK_VALUE, MOCK_VALUE);
|
||||||
|
runBackendStepOutput.addValue("noOfPeopleGreeted", runBackendStepInput.getRecords().size());
|
||||||
|
|
||||||
if("there".equalsIgnoreCase(runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX)))
|
if("there".equalsIgnoreCase(runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX)))
|
||||||
{
|
{
|
||||||
throw (new QException("You said Hello There, didn't you..."));
|
throw (new QException("You said Hello There, didn't you..."));
|
||||||
|
@ -22,10 +22,13 @@
|
|||||||
package com.kingsrook.qqq.backend.core.state;
|
package com.kingsrook.qqq.backend.core.state;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public abstract class AbstractStateKey
|
public abstract class AbstractStateKey implements Serializable
|
||||||
{
|
{
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Make the key give a unique string to identify itself.
|
** Make the key give a unique string to identify itself.
|
||||||
|
@ -31,5 +31,7 @@ package com.kingsrook.qqq.backend.core.state;
|
|||||||
public enum StateType
|
public enum StateType
|
||||||
{
|
{
|
||||||
PROCESS_STATUS,
|
PROCESS_STATUS,
|
||||||
ASYNC_JOB_STATUS
|
ASYNC_JOB_STATUS,
|
||||||
|
ASYNC_JOB_RESULT,
|
||||||
|
UPLOADED_FILE
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.state;
|
package com.kingsrook.qqq.backend.core.state;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ import java.util.UUID;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class UUIDAndTypeStateKey extends AbstractStateKey
|
public class UUIDAndTypeStateKey extends AbstractStateKey implements Serializable
|
||||||
{
|
{
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final StateType stateType;
|
private final StateType stateType;
|
||||||
|
@ -109,6 +109,7 @@ public class RunBackendStepActionTest
|
|||||||
XYZ""";
|
XYZ""";
|
||||||
case HTML -> "<b>Oh my</b>";
|
case HTML -> "<b>Oh my</b>";
|
||||||
case PASSWORD -> "myPa**word";
|
case PASSWORD -> "myPa**word";
|
||||||
|
case BLOB -> new byte[] { 1, 2, 3, 4 };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return (rs);
|
return (rs);
|
||||||
|
@ -26,10 +26,14 @@ import java.util.List;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -53,8 +57,28 @@ class DeleteActionTest
|
|||||||
request.setPrimaryKeys(List.of(1, 2));
|
request.setPrimaryKeys(List.of(1, 2));
|
||||||
DeleteOutput result = new DeleteAction().execute(request);
|
DeleteOutput result = new DeleteAction().execute(request);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
assertEquals(2, result.getRecords().size());
|
assertEquals(2, result.getDeletedRecordCount());
|
||||||
// todo - add errors to QRecord? assertTrue(result.getRecords().stream().allMatch(r -> r.getErrors() == null));
|
assertTrue(CollectionUtils.nullSafeIsEmpty(result.getRecordsWithErrors()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testErrorIfBothPrimaryKeysAndFilter()
|
||||||
|
{
|
||||||
|
DeleteInput request = new DeleteInput(TestUtils.defineInstance());
|
||||||
|
request.setSession(TestUtils.getMockSession());
|
||||||
|
request.setTableName("person");
|
||||||
|
request.setPrimaryKeys(List.of(1, 2));
|
||||||
|
request.setQueryFilter(new QQueryFilter());
|
||||||
|
|
||||||
|
assertThrows(QException.class, () ->
|
||||||
|
{
|
||||||
|
new DeleteAction().execute(request);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.processes.implementations.bulk.delete;
|
||||||
|
|
||||||
|
|
||||||
|
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.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BulkDeleteStoreStep
|
||||||
|
*******************************************************************************/
|
||||||
|
class BulkDeleteStoreStepTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testWithoutFilter() throws QException
|
||||||
|
{
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.setRecords(TestUtils.queryTable(TestUtils.defineTablePerson().getName()));
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkDeleteStoreStep().run(stepInput, stepOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testWithFilter() throws QException
|
||||||
|
{
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.addValue("queryFilterJSON", JsonUtils.toJson(new QQueryFilter()));
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkDeleteStoreStep().run(stepInput, stepOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.edit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
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.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BulkEditReceiveValuesStep
|
||||||
|
*******************************************************************************/
|
||||||
|
class BulkEditReceiveValuesStepTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.addValue(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS, "firstName,email,birthDate");
|
||||||
|
stepInput.addValue("firstName", "Johnny");
|
||||||
|
stepInput.addValue("email", null);
|
||||||
|
stepInput.addValue("birthDate", "1909-01-09");
|
||||||
|
List<QRecord> records = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.setRecords(records);
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkEditReceiveValuesStep().run(stepInput, stepOutput);
|
||||||
|
|
||||||
|
String valuesBeingUpdated = stepOutput.getValueString(BulkEditReceiveValuesStep.FIELD_VALUES_BEING_UPDATED);
|
||||||
|
assertThat(valuesBeingUpdated).matches("(?s).*First Name.*Johnny.*");
|
||||||
|
assertThat(valuesBeingUpdated).matches("(?s).*Email will be cleared.*");
|
||||||
|
assertThat(valuesBeingUpdated).matches("(?s).*Birth Date.*1909-01-09.*");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for(QRecord record : stepOutput.getRecords())
|
||||||
|
{
|
||||||
|
assertEquals("Johnny", record.getValueString("firstName"));
|
||||||
|
assertNull(record.getValue("email"));
|
||||||
|
// todo value utils needed in getValueDate... assertEquals(LocalDate.of(1909, 1, 9), record.getValueDate("birthDate"));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
assertEquals(records.size(), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.processes.implementations.bulk.edit;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
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.data.QRecord;
|
||||||
|
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.assertNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BulkEditStoreRecordsStep
|
||||||
|
*******************************************************************************/
|
||||||
|
class BulkEditStoreRecordsStepTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.addValue(BulkEditReceiveValuesStep.FIELD_ENABLED_FIELDS, "firstName,email,birthDate");
|
||||||
|
stepInput.addValue("firstName", "Johnny");
|
||||||
|
stepInput.addValue("email", null);
|
||||||
|
stepInput.addValue("birthDate", "1909-01-09");
|
||||||
|
List<QRecord> records = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.setRecords(records);
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkEditStoreRecordsStep().run(stepInput, stepOutput);
|
||||||
|
|
||||||
|
assertRecordValues(stepOutput.getRecords());
|
||||||
|
|
||||||
|
// re-fetch the records, make sure they are updated.
|
||||||
|
// but since Mock backend doesn't actually update them, we can't do this..
|
||||||
|
// todo - implement an in-memory backend, that would do this.
|
||||||
|
// List<QRecord> updatedRecords = TestUtils.queryTable(TestUtils.defineTablePerson().getName());
|
||||||
|
// assertRecordValues(updatedRecords);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void assertRecordValues(List<QRecord> records)
|
||||||
|
{
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
assertEquals("Johnny", record.getValueString("firstName"));
|
||||||
|
assertNull(record.getValue("email"));
|
||||||
|
// todo value utils needed in getValueDate... assertEquals(LocalDate.of(1909, 1, 9), record.getValueDate("birthDate"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.insert;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||||
|
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.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.TempFileStateProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BulkInsertReceiveFileStep
|
||||||
|
*******************************************************************************/
|
||||||
|
class BulkInsertReceiveFileStepTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// create an uploaded file, similar to how an http server may //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
QUploadedFile qUploadedFile = new QUploadedFile();
|
||||||
|
qUploadedFile.setBytes((TestUtils.getPersonCsvHeaderUsingLabels() + TestUtils.getPersonCsvRow1() + TestUtils.getPersonCsvRow2()).getBytes());
|
||||||
|
qUploadedFile.setFilename("test.csv");
|
||||||
|
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.UPLOADED_FILE);
|
||||||
|
TempFileStateProvider.getInstance().put(key, qUploadedFile);
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// setup and run the step //
|
||||||
|
////////////////////////////
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.addValue("uploadedFileKey", key);
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkInsertReceiveFileStep().run(stepInput, stepOutput);
|
||||||
|
|
||||||
|
List<QRecord> records = stepOutput.getRecords();
|
||||||
|
assertEquals(2, records.size());
|
||||||
|
assertEquals("John", records.get(0).getValueString("firstName"));
|
||||||
|
assertEquals("Jane", records.get(1).getValueString("firstName"));
|
||||||
|
assertNull(records.get(0).getValue("id"));
|
||||||
|
assertNull(records.get(1).getValue("id"));
|
||||||
|
|
||||||
|
assertEquals(2, stepOutput.getValueInteger("noOfFileRows"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* 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.bulk.insert;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.QUploadedFile;
|
||||||
|
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.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.StateType;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.TempFileStateProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.state.UUIDAndTypeStateKey;
|
||||||
|
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 BulkInsertStoreRecordsStep
|
||||||
|
*******************************************************************************/
|
||||||
|
class BulkInsertStoreRecordsStepTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// create an uploaded file, similar to how an http server may //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
QUploadedFile qUploadedFile = new QUploadedFile();
|
||||||
|
qUploadedFile.setBytes((TestUtils.getPersonCsvHeaderUsingLabels() + TestUtils.getPersonCsvRow1() + TestUtils.getPersonCsvRow2()).getBytes());
|
||||||
|
qUploadedFile.setFilename("test.csv");
|
||||||
|
UUIDAndTypeStateKey key = new UUIDAndTypeStateKey(StateType.UPLOADED_FILE);
|
||||||
|
TempFileStateProvider.getInstance().put(key, qUploadedFile);
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// setup and run the step //
|
||||||
|
////////////////////////////
|
||||||
|
RunBackendStepInput stepInput = new RunBackendStepInput(TestUtils.defineInstance());
|
||||||
|
stepInput.setSession(TestUtils.getMockSession());
|
||||||
|
stepInput.setTableName(TestUtils.defineTablePerson().getName());
|
||||||
|
stepInput.addValue("uploadedFileKey", key);
|
||||||
|
|
||||||
|
RunBackendStepOutput stepOutput = new RunBackendStepOutput();
|
||||||
|
new BulkInsertStoreRecordsStep().run(stepInput, stepOutput);
|
||||||
|
|
||||||
|
List<QRecord> records = stepOutput.getRecords();
|
||||||
|
assertEquals(2, records.size());
|
||||||
|
assertEquals("John", records.get(0).getValueString("firstName"));
|
||||||
|
assertEquals("Jane", records.get(1).getValueString("firstName"));
|
||||||
|
assertNotNull(records.get(0).getValue("id"));
|
||||||
|
assertNotNull(records.get(1).getValue("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,7 +25,12 @@ package com.kingsrook.qqq.backend.core.utils;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
|
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
|
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
import com.kingsrook.qqq.backend.core.adapters.QInstanceAdapter;
|
||||||
|
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.processes.implementations.mock.MockBackendStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
@ -136,9 +141,9 @@ public class TestUtils
|
|||||||
.withLabel("Person")
|
.withLabel("Person")
|
||||||
.withBackendName(DEFAULT_BACKEND_NAME)
|
.withBackendName(DEFAULT_BACKEND_NAME)
|
||||||
.withPrimaryKeyField("id")
|
.withPrimaryKeyField("id")
|
||||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME))
|
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME))
|
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
||||||
@ -306,4 +311,67 @@ public class TestUtils
|
|||||||
MockAuthenticationModule mockAuthenticationModule = new MockAuthenticationModule();
|
MockAuthenticationModule mockAuthenticationModule = new MockAuthenticationModule();
|
||||||
return (mockAuthenticationModule.createSession(null));
|
return (mockAuthenticationModule.createSession(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static List<QRecord> queryTable(String tableName) throws QException
|
||||||
|
{
|
||||||
|
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
|
||||||
|
queryInput.setSession(TestUtils.getMockSession());
|
||||||
|
queryInput.setTableName(tableName);
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
return (queryOutput.getRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String getPersonCsvHeader()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
"id","createDate","modifyDate","firstName","lastName","birthDate","email"\r
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String getPersonCsvHeaderUsingLabels()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
"Id","Create Date","Modify Date","First Name","Last Name","Birth Date","Email"\r
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String getPersonCsvRow1()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","John","Doe","1980-01-01","john@doe.com"\r
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static String getPersonCsvRow2()
|
||||||
|
{
|
||||||
|
return ("""
|
||||||
|
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","Jane","Doe","1981-01-01","john@doe.com"\r
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user