Implement min/max records enforcement

This commit is contained in:
2024-05-17 16:16:26 -05:00
parent 8816bc89c3
commit e10a1e40da
2 changed files with 116 additions and 18 deletions

View File

@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
@ -82,7 +83,7 @@ public class RunBackendStepAction
//////////////////////////////////////////////////////////////////////////////////////
// ensure input data is set as needed - use callback object to get anything missing //
//////////////////////////////////////////////////////////////////////////////////////
ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData);
ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData, process);
ensureInputFieldsAreInRequest(runBackendStepInput, backendStepMetaData);
////////////////////////////////////////////////////////////////////
@ -167,7 +168,7 @@ public class RunBackendStepAction
** check if this step uses a record list - and if so, if we need to get one
** via the callback
*******************************************************************************/
private void ensureRecordsAreInRequest(RunBackendStepInput runBackendStepInput, QBackendStepMetaData step) throws QException
private void ensureRecordsAreInRequest(RunBackendStepInput runBackendStepInput, QBackendStepMetaData step, QProcessMetaData process) throws QException
{
QFunctionInputMetaData inputMetaData = step.getInputMetaData();
if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null)
@ -190,9 +191,44 @@ public class RunBackendStepAction
queryInput.setFilter(callback.getQueryFilter());
//////////////////////////////////////////////////////////////////////////////////////////
// if process has a max-no of records, set a limit on the process of that number plus 1 //
// (the plus 1 being so we can see "oh, you selected more than that many; error!" //
//////////////////////////////////////////////////////////////////////////////////////////
if(process.getMaxInputRecords() != null)
{
if(callback.getQueryFilter() == null)
{
queryInput.setFilter(new QQueryFilter());
}
queryInput.getFilter().setLimit(process.getMaxInputRecords() + 1);
}
QueryOutput queryOutput = new QueryAction().execute(queryInput);
runBackendStepInput.setRecords(queryOutput.getRecords());
// todo - handle 0 results found?
////////////////////////////////////////////////////////////////////////////////
// if process defines a max, and more than the max were found, throw an error //
////////////////////////////////////////////////////////////////////////////////
if(process.getMaxInputRecords() != null)
{
if(queryOutput.getRecords().size() > process.getMaxInputRecords())
{
throw (new QUserFacingException("Too many records were selected for this process. At most, only " + process.getMaxInputRecords() + " can be selected."));
}
}
/////////////////////////////////////////////////////////////////////////////////
// if process defines a min, and fewer than the min were found, throw an error //
/////////////////////////////////////////////////////////////////////////////////
if(process.getMinInputRecords() != null)
{
if(queryOutput.getRecords().size() < process.getMinInputRecords())
{
throw (new QUserFacingException("Too few records were selected for this process. At least " + process.getMinInputRecords() + " must be selected."));
}
}
}
}
}

View File

@ -27,13 +27,21 @@ import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -53,7 +61,7 @@ public class RunBackendStepActionTest extends BaseTest
{
TestCallback callback = new TestCallback();
RunBackendStepInput request = new RunBackendStepInput();
request.setProcessName("greet");
request.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
request.setStepName("prepare");
request.setCallback(callback);
RunBackendStepOutput result = new RunBackendStepAction().execute(request);
@ -67,6 +75,60 @@ public class RunBackendStepActionTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMinMaxInputRecords() throws QException
{
////////////////////////////////////////////
// put a min-input-records on the process //
////////////////////////////////////////////
QContext.getQInstance().getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).withMinInputRecords(5);
//////////////////////////////////////////////////////////////////////////////////////
// insert fewer than that min - then run w/ non-filtered filter, and assert we fail //
//////////////////////////////////////////////////////////////////////////////////////
for(int i = 0; i < 3; i++)
{
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecord(new QRecord().withValue("firstName", String.valueOf(i))));
}
Supplier<RunBackendStepInput> inputSupplier = () ->
{
RunBackendStepInput input = new RunBackendStepInput();
input.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
input.setStepName("prepare");
input.setCallback(QProcessCallbackFactory.forFilter(new QQueryFilter()));
return (input);
};
assertThatThrownBy(() -> new RunBackendStepAction().execute(inputSupplier.get()))
.isInstanceOf(QUserFacingException.class)
.hasMessageContaining("Too few records");
////////////////////////////////////////////////////
// insert a few more - and then it should succeed //
////////////////////////////////////////////////////
for(int i = 3; i < 10; i++)
{
new InsertAction().execute(new InsertInput(TestUtils.TABLE_NAME_PERSON_MEMORY).withRecord(new QRecord().withValue("firstName", String.valueOf(i))));
}
new RunBackendStepAction().execute(inputSupplier.get());
////////////////////////////////////////////////////////////
// now put a max on the process, and it should fail again //
////////////////////////////////////////////////////////////
QContext.getQInstance().getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).withMaxInputRecords(8);
assertThatThrownBy(() -> new RunBackendStepAction().execute(inputSupplier.get()))
.isInstanceOf(QUserFacingException.class)
.hasMessageContaining("Too many records");
}
/*******************************************************************************
**
*******************************************************************************/