Add concept of inputSource on insert/update/delete actions.

This commit is contained in:
2023-05-19 16:34:26 -05:00
parent 486a942fdc
commit e10af188ef
20 changed files with 355 additions and 4 deletions

View File

@ -168,7 +168,12 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
ValueBehaviorApplier.applyFieldBehaviors(insertInput.getInstance(), table, insertInput.getRecords());
setErrorsIfUniqueKeyErrors(insertInput, table);
validateRequiredFields(insertInput);
if(insertInput.getInputSource().shouldValidateRequiredFields())
{
validateRequiredFields(insertInput);
}
ValidateRecordSecurityLockHelper.validateSecurityFields(insertInput.getTable(), insertInput.getRecords(), ValidateRecordSecurityLockHelper.Action.INSERT);
///////////////////////////////////////////////////////////////////////////

View File

@ -187,7 +187,11 @@ public class UpdateAction
validateRecordsExistAndCanBeAccessed(updateInput, oldRecordList.get());
}
validateRequiredFields(updateInput);
if(updateInput.getInputSource().shouldValidateRequiredFields())
{
validateRequiredFields(updateInput);
}
ValidateRecordSecurityLockHelper.validateSecurityFields(table, updateInput.getRecords(), ValidateRecordSecurityLockHelper.Action.UPDATE);
///////////////////////////////////////////////////////////////////////////

View File

@ -70,9 +70,9 @@ import com.kingsrook.qqq.backend.core.processes.implementations.bulk.delete.Bulk
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.edit.BulkEditTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertExtractStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.BulkInsertTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -697,7 +697,7 @@ public class QInstanceEnricher
QProcessMetaData process = StreamedETLWithFrontendProcess.defineProcessMetaData(
BulkInsertExtractStep.class,
BulkInsertTransformStep.class,
LoadViaInsertStep.class,
BulkInsertLoadStep.class,
values
)
.withName(processName)

View File

@ -0,0 +1,43 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.tables;
/*******************************************************************************
** interface to define input sources - idea being, so QQQ can have its standard
** ones (see QInputSource), but applications can define their own as well.
**
** We might imagine things like a user's session dictating what InputSource
** gets passed into all DML actions. Or perhaps API meta-data, or just a method
** on QInstance in the future?
**
** We might imagine, maybe, more methods growing in the future...
*******************************************************************************/
public interface InputSource
{
/*******************************************************************************
**
*******************************************************************************/
boolean shouldValidateRequiredFields();
}

View File

@ -0,0 +1,56 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.actions.tables;
/*******************************************************************************
** QQQ standard input sources -- the system, or users.
*******************************************************************************/
public enum QInputSource implements InputSource
{
SYSTEM(true),
USER(true);
private final boolean shouldValidateRequiredFields;
/*******************************************************************************
**
*******************************************************************************/
QInputSource(boolean shouldValidateRequiredFields)
{
this.shouldValidateRequiredFields = shouldValidateRequiredFields;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public boolean shouldValidateRequiredFields()
{
return (this.shouldValidateRequiredFields);
}
}

View File

@ -26,6 +26,8 @@ import java.io.Serializable;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.utils.collections.MutableList;
@ -39,6 +41,7 @@ public class DeleteInput extends AbstractTableActionInput
private QBackendTransaction transaction;
private List<Serializable> primaryKeys;
private QQueryFilter queryFilter;
private InputSource inputSource = QInputSource.SYSTEM;
@ -154,4 +157,35 @@ public class DeleteInput extends AbstractTableActionInput
return this;
}
/*******************************************************************************
** Getter for inputSource
*******************************************************************************/
public InputSource getInputSource()
{
return (this.inputSource);
}
/*******************************************************************************
** Setter for inputSource
*******************************************************************************/
public void setInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
}
/*******************************************************************************
** Fluent setter for inputSource
*******************************************************************************/
public DeleteInput withInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
return (this);
}
}

View File

@ -25,6 +25,8 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.insert;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -36,6 +38,7 @@ public class InsertInput extends AbstractTableActionInput
{
private QBackendTransaction transaction;
private List<QRecord> records;
private InputSource inputSource = QInputSource.SYSTEM;
private boolean skipUniqueKeyCheck = false;
@ -182,4 +185,35 @@ public class InsertInput extends AbstractTableActionInput
return (this);
}
/*******************************************************************************
** Getter for inputSource
*******************************************************************************/
public InputSource getInputSource()
{
return (this.inputSource);
}
/*******************************************************************************
** Setter for inputSource
*******************************************************************************/
public void setInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
}
/*******************************************************************************
** Fluent setter for inputSource
*******************************************************************************/
public InsertInput withInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
return (this);
}
}

View File

@ -25,6 +25,8 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.update;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -36,6 +38,7 @@ public class UpdateInput extends AbstractTableActionInput
{
private QBackendTransaction transaction;
private List<QRecord> records;
private InputSource inputSource = QInputSource.SYSTEM;
////////////////////////////////////////////////////////////////////////////////////////////
// allow a caller to specify that they KNOW this optimization (e.g., in SQL) can be made. //
@ -219,4 +222,35 @@ public class UpdateInput extends AbstractTableActionInput
return (this);
}
/*******************************************************************************
** Getter for inputSource
*******************************************************************************/
public InputSource getInputSource()
{
return (this.inputSource);
}
/*******************************************************************************
** Setter for inputSource
*******************************************************************************/
public void setInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
}
/*******************************************************************************
** Fluent setter for inputSource
*******************************************************************************/
public UpdateInput withInputSource(InputSource inputSource)
{
this.inputSource = inputSource;
return (this);
}
}

View File

@ -36,6 +36,8 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
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.Status;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -60,6 +62,17 @@ public class BulkDeleteLoadStep extends LoadViaDeleteStep implements ProcessSumm
/*******************************************************************************
**
*******************************************************************************/
@Override
protected InputSource getInputSource()
{
return (QInputSource.USER);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -33,6 +33,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
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.Status;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -103,6 +104,7 @@ public class BulkDeleteTransformStep extends AbstractTransformStep
///////////////////////////////////////////////////////////////////////
DeleteAction deleteAction = new DeleteAction();
DeleteInput deleteInput = new DeleteInput();
deleteInput.setInputSource(QInputSource.USER);
deleteInput.setTableName(runBackendStepInput.getTableName());
deleteInput.setPrimaryKeys(runBackendStepInput.getRecords().stream().map(r -> r.getValue(primaryKeyField)).toList());
List<QRecord> validationResultRecords = deleteAction.performValidations(deleteInput, Optional.of(runBackendStepInput.getRecords()), true);

View File

@ -31,6 +31,8 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
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.Status;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaUpdateStep;
@ -56,6 +58,17 @@ public class BulkEditLoadStep extends LoadViaUpdateStep implements ProcessSummar
/*******************************************************************************
**
*******************************************************************************/
@Override
protected InputSource getInputSource()
{
return (QInputSource.USER);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
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.Status;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
@ -167,6 +168,7 @@ public class BulkEditTransformStep extends AbstractTransformStep
// run the validation - critically - in preview mode (boolean param) //
///////////////////////////////////////////////////////////////////////
UpdateInput updateInput = new UpdateInput();
updateInput.setInputSource(QInputSource.USER);
updateInput.setTableName(table.getName());
updateInput.setRecords(recordsForValidation);
new UpdateAction().performValidations(updateInput, Optional.of(runBackendStepInput.getRecords()), true);

View File

@ -0,0 +1,45 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2023. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert;
import com.kingsrook.qqq.backend.core.model.actions.tables.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep;
/*******************************************************************************
**
*******************************************************************************/
public class BulkInsertLoadStep extends LoadViaInsertStep
{
/*******************************************************************************
**
*******************************************************************************/
@Override
protected InputSource getInputSource()
{
return (QInputSource.USER);
}
}

View File

@ -38,6 +38,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine
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.Status;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -182,6 +183,7 @@ public class BulkInsertTransformStep extends AbstractTransformStep
/////////////////////////////////////////////////////////////////////////////////
InsertAction insertAction = new InsertAction();
InsertInput insertInput = new InsertInput();
insertInput.setInputSource(QInputSource.USER);
insertInput.setTableName(runBackendStepInput.getTableName());
insertInput.setRecords(recordsWithoutUkErrors);
insertInput.setSkipUniqueKeyCheck(true);

View File

@ -30,6 +30,8 @@ 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.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
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.insert.InsertInput;
@ -46,6 +48,16 @@ public class LoadViaDeleteStep extends AbstractLoadStep
/*******************************************************************************
**
*******************************************************************************/
protected InputSource getInputSource()
{
return (QInputSource.SYSTEM);
}
/*******************************************************************************
** Execute the backend step - using the request as input, and the result as output.
**
@ -56,6 +68,7 @@ public class LoadViaDeleteStep extends AbstractLoadStep
QTableMetaData table = runBackendStepInput.getTable();
DeleteInput deleteInput = new DeleteInput();
deleteInput.setInputSource(getInputSource());
deleteInput.setTableName(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
deleteInput.setPrimaryKeys(runBackendStepInput.getRecords().stream().map(r -> r.getValue(table.getPrimaryKeyField())).collect(Collectors.toList()));
deleteInput.setAsyncJobCallback(runBackendStepInput.getAsyncJobCallback());

View File

@ -31,6 +31,8 @@ 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.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
@ -56,6 +58,16 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
/*******************************************************************************
**
*******************************************************************************/
protected InputSource getInputSource()
{
return (QInputSource.SYSTEM);
}
/*******************************************************************************
** Execute the backend step - using the request as input, and the result as output.
**
@ -79,6 +91,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
if(CollectionUtils.nullSafeHasContents(recordsToInsert))
{
InsertInput insertInput = new InsertInput();
insertInput.setInputSource(getInputSource());
insertInput.setTableName(tableMetaData.getName());
insertInput.setRecords(recordsToInsert);
getTransaction().ifPresent(insertInput::setTransaction);
@ -96,6 +109,7 @@ public class LoadViaInsertOrUpdateStep extends AbstractLoadStep
if(CollectionUtils.nullSafeHasContents(recordsToUpdate))
{
UpdateInput updateInput = new UpdateInput();
updateInput.setInputSource(getInputSource());
updateInput.setTableName(tableMetaData.getName());
updateInput.setRecords(recordsToUpdate);
getTransaction().ifPresent(updateInput::setTransaction);

View File

@ -28,6 +28,8 @@ 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.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
@ -43,6 +45,16 @@ public class LoadViaInsertStep extends AbstractLoadStep
/*******************************************************************************
**
*******************************************************************************/
protected InputSource getInputSource()
{
return (QInputSource.SYSTEM);
}
/*******************************************************************************
** Execute the backend step - using the request as input, and the result as output.
**
@ -51,6 +63,7 @@ public class LoadViaInsertStep extends AbstractLoadStep
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
InsertInput insertInput = new InsertInput();
insertInput.setInputSource(getInputSource());
insertInput.setTableName(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
insertInput.setRecords(runBackendStepInput.getRecords());
getTransaction().ifPresent(insertInput::setTransaction);

View File

@ -29,6 +29,8 @@ 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.InputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.QInputSource;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
@ -44,6 +46,16 @@ public class LoadViaUpdateStep extends AbstractLoadStep
/*******************************************************************************
**
*******************************************************************************/
protected InputSource getInputSource()
{
return (QInputSource.SYSTEM);
}
/*******************************************************************************
** Execute the backend step - using the request as input, and the result as output.
**
@ -52,6 +64,7 @@ public class LoadViaUpdateStep extends AbstractLoadStep
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
UpdateInput updateInput = new UpdateInput();
updateInput.setInputSource(getInputSource());
updateInput.setTableName(runBackendStepInput.getValueString(FIELD_DESTINATION_TABLE));
updateInput.setRecords(runBackendStepInput.getRecords());
getTransaction().ifPresent(updateInput::setTransaction);