mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Implement min/max records enforcement
This commit is contained in:
@ -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.logging.QLogger;
|
||||||
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.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
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 //
|
// ensure input data is set as needed - use callback object to get anything missing //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData);
|
ensureRecordsAreInRequest(runBackendStepInput, backendStepMetaData, process);
|
||||||
ensureInputFieldsAreInRequest(runBackendStepInput, backendStepMetaData);
|
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
|
** check if this step uses a record list - and if so, if we need to get one
|
||||||
** via the callback
|
** 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();
|
QFunctionInputMetaData inputMetaData = step.getInputMetaData();
|
||||||
if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null)
|
if(inputMetaData != null && inputMetaData.getRecordListMetaData() != null)
|
||||||
@ -190,9 +191,44 @@ public class RunBackendStepAction
|
|||||||
|
|
||||||
queryInput.setFilter(callback.getQueryFilter());
|
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);
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
runBackendStepInput.setRecords(queryOutput.getRecords());
|
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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,21 @@ import java.math.BigDecimal;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
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.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.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.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
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.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -53,7 +61,7 @@ public class RunBackendStepActionTest extends BaseTest
|
|||||||
{
|
{
|
||||||
TestCallback callback = new TestCallback();
|
TestCallback callback = new TestCallback();
|
||||||
RunBackendStepInput request = new RunBackendStepInput();
|
RunBackendStepInput request = new RunBackendStepInput();
|
||||||
request.setProcessName("greet");
|
request.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
|
||||||
request.setStepName("prepare");
|
request.setStepName("prepare");
|
||||||
request.setCallback(callback);
|
request.setCallback(callback);
|
||||||
RunBackendStepOutput result = new RunBackendStepAction().execute(request);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -100,20 +162,20 @@ public class RunBackendStepActionTest extends BaseTest
|
|||||||
for(QFieldMetaData field : fields)
|
for(QFieldMetaData field : fields)
|
||||||
{
|
{
|
||||||
rs.put(field.getName(), switch(field.getType())
|
rs.put(field.getName(), switch(field.getType())
|
||||||
{
|
{
|
||||||
case STRING -> "ABC";
|
case STRING -> "ABC";
|
||||||
case INTEGER -> 42;
|
case INTEGER -> 42;
|
||||||
case LONG -> 42L;
|
case LONG -> 42L;
|
||||||
case DECIMAL -> new BigDecimal("47");
|
case DECIMAL -> new BigDecimal("47");
|
||||||
case BOOLEAN -> true;
|
case BOOLEAN -> true;
|
||||||
case DATE, TIME, DATE_TIME -> null;
|
case DATE, TIME, DATE_TIME -> null;
|
||||||
case TEXT -> """
|
case TEXT -> """
|
||||||
ABC
|
ABC
|
||||||
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 };
|
case BLOB -> new byte[] { 1, 2, 3, 4 };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return (rs);
|
return (rs);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user