mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
QQQ-40 Initial working POC
This commit is contained in:
@ -0,0 +1,233 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.automation.polling;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
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.tables.insert.InsertInput;
|
||||
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.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
|
||||
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.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for PollingAutomationExecutor
|
||||
*******************************************************************************/
|
||||
class PollingAutomationExecutorTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testInsert() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// insert 2 people - one who should be updated by the check-age automation //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "John").withValue("birthDate", LocalDate.of(1970, Month.JANUARY, 1)),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "Jim").withValue("birthDate", LocalDate.now().minusDays(30))
|
||||
));
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// have the polling executor run "for awhile" //
|
||||
////////////////////////////////////////////////
|
||||
runPollingAutomationExecutorForAwhile(qInstance);
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// query for the records - assert their status //
|
||||
/////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput(qInstance);
|
||||
queryInput.setSession(new QSession());
|
||||
queryInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(2, queryOutput.getRecords().size());
|
||||
|
||||
Optional<QRecord> optionalPerson1 = queryOutput.getRecords().stream().filter(r -> r.getValueInteger("id") == 1).findFirst();
|
||||
assertThat(optionalPerson1).isPresent();
|
||||
QRecord person1 = optionalPerson1.get();
|
||||
assertThat(person1.getValueString("firstName")).isEqualTo("John");
|
||||
assertThat(person1.getValueInteger(TestUtils.standardQqqAutomationStatusField().getName())).isEqualTo(AutomationStatus.OK.getId());
|
||||
|
||||
Optional<QRecord> optionalPerson2 = queryOutput.getRecords().stream().filter(r -> r.getValueInteger("id") == 2).findFirst();
|
||||
assertThat(optionalPerson2).isPresent();
|
||||
QRecord person2 = optionalPerson2.get();
|
||||
assertThat(person2.getValueString("firstName")).isEqualTo("Jim" + TestUtils.CheckAge.SUFFIX_FOR_MINORS);
|
||||
assertThat(person2.getValueInteger(TestUtils.standardQqqAutomationStatusField().getName())).isEqualTo(AutomationStatus.OK.getId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUpdate() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// insert 2 people - one who should be logged by logger-on-update automation //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "Tim"),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "Darin")
|
||||
));
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// have the polling executor run "for awhile" //
|
||||
////////////////////////////////////////////////
|
||||
runPollingAutomationExecutorForAwhile(qInstance);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// assert that the update-automation didn't run //
|
||||
//////////////////////////////////////////////////
|
||||
assertThat(TestUtils.LogPersonUpdate.updatedIds).isNullOrEmpty();
|
||||
|
||||
UpdateInput updateInput = new UpdateInput(qInstance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
updateInput.setRecords(List.of(
|
||||
new QRecord().withValue("id", 1).withValue("lastName", "now with a LastName"),
|
||||
new QRecord().withValue("id", 2).withValue("lastName", "now with a LastName")
|
||||
));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// have the polling executor run "for awhile" //
|
||||
////////////////////////////////////////////////
|
||||
runPollingAutomationExecutorForAwhile(qInstance);
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// assert that the update-automation DID run now //
|
||||
///////////////////////////////////////////////////
|
||||
assertThat(TestUtils.LogPersonUpdate.updatedIds).contains(2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSessionSupplier() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// make the person-memory table's insert-action run a class in here //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.getAutomationDetails().getActions().get(0)
|
||||
.setCodeReference(new QCodeReference(CaptureSessionIdAutomationHandler.class));
|
||||
|
||||
/////////////////////
|
||||
// insert a person //
|
||||
/////////////////////
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "Tim")
|
||||
));
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
QSession session = new QSession();
|
||||
session.setIdReference(uuid);
|
||||
PollingAutomationExecutor.getInstance().setSessionSupplier(() -> session);
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// have the polling executor run "for awhile" //
|
||||
////////////////////////////////////////////////
|
||||
runPollingAutomationExecutorForAwhile(qInstance);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// assert that the uuid we put in our session was present in the CaptureSessionIdAutomationHandler //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertThat(CaptureSessionIdAutomationHandler.sessionId).isEqualTo(uuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class CaptureSessionIdAutomationHandler extends RecordAutomationHandler
|
||||
{
|
||||
static String sessionId;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void execute(RecordAutomationInput recordAutomationInput) throws QException
|
||||
{
|
||||
sessionId = recordAutomationInput.getSession().getIdReference();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void runPollingAutomationExecutorForAwhile(QInstance qInstance)
|
||||
{
|
||||
PollingAutomationExecutor pollingAutomationExecutor = PollingAutomationExecutor.getInstance();
|
||||
pollingAutomationExecutor.setInitialDelayMillis(0);
|
||||
pollingAutomationExecutor.setDelayMillis(100);
|
||||
pollingAutomationExecutor.start(qInstance, TestUtils.POLLING_AUTOMATION);
|
||||
SleepUtils.sleep(1, TimeUnit.SECONDS);
|
||||
pollingAutomationExecutor.stop();
|
||||
}
|
||||
|
||||
}
|
@ -30,6 +30,9 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
@ -43,6 +46,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -162,11 +166,13 @@ class QInstanceValidatorTest
|
||||
qInstance.getBackend("default").setName("notDefault");
|
||||
qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).setName("notGreetPeople");
|
||||
qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE).setName("notStates");
|
||||
qInstance.getAutomationProvider(TestUtils.POLLING_AUTOMATION).setName("notPolling");
|
||||
},
|
||||
"Inconsistent naming for table",
|
||||
"Inconsistent naming for backend",
|
||||
"Inconsistent naming for process",
|
||||
"Inconsistent naming for possibleValueSource"
|
||||
"Inconsistent naming for possibleValueSource",
|
||||
"Inconsistent naming for automationProvider"
|
||||
);
|
||||
}
|
||||
|
||||
@ -296,7 +302,7 @@ class QInstanceValidatorTest
|
||||
"Instance of CodeReference could not be created");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").withCustomizer(TableCustomizers.POST_QUERY_RECORD.getRole(), new QCodeReference(CustomizerThatIsNotAFunction.class, QCodeUsage.CUSTOMIZER)),
|
||||
"CodeReference could not be casted");
|
||||
"CodeReference is not of the expected type");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").withCustomizer(TableCustomizers.POST_QUERY_RECORD.getRole(), new QCodeReference(CustomizerFunctionWithIncorrectTypeParameters.class, QCodeUsage.CUSTOMIZER)),
|
||||
"Error validating customizer type parameters");
|
||||
@ -649,6 +655,230 @@ class QInstanceValidatorTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testAutomationProviderType()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getAutomationProvider(TestUtils.POLLING_AUTOMATION).setType(null),
|
||||
"Missing type for automationProvider");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationProviderName()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().setProviderName(null),
|
||||
"is missing a providerName");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().setProviderName(""),
|
||||
"is missing a providerName");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().setProviderName("notARealProvider"),
|
||||
"unrecognized providerName");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationStatusTracking()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().setStatusTracking(null),
|
||||
"do not have statusTracking");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationStatusTrackingType()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getStatusTracking().setType(null),
|
||||
"statusTracking is missing a type");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationStatusTrackingFieldName()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getStatusTracking().setFieldName(null),
|
||||
"missing its fieldName");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getStatusTracking().setFieldName(""),
|
||||
"missing its fieldName");
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// todo - make sure it's a field in the table?? //
|
||||
//////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionsNames()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> getAction0(qInstance).setName(null),
|
||||
"action missing a name");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> getAction0(qInstance).setName(""),
|
||||
"action missing a name");
|
||||
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
List<TableAutomationAction> actions = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getActions();
|
||||
actions.add(actions.get(0));
|
||||
},
|
||||
"more than one action named");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionTriggerEvent()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> getAction0(qInstance).setTriggerEvent(null),
|
||||
"missing a triggerEvent");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionCodeReference()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> getAction0(qInstance).setCodeReference(new QCodeReference()),
|
||||
"missing a code reference name", "missing a code type");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> getAction0(qInstance).setCodeReference(new QCodeReference(TestUtils.CustomPossibleValueSource.class)),
|
||||
"is not of the expected type");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionProcessName()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setCodeReference(null);
|
||||
action.setProcessName("notAProcess");
|
||||
},
|
||||
"unrecognized processName");
|
||||
|
||||
assertValidationSuccess((qInstance) ->
|
||||
{
|
||||
qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setCodeReference(null);
|
||||
action.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
|
||||
});
|
||||
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setCodeReference(null);
|
||||
action.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
|
||||
},
|
||||
"different table");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionCodeReferenceAndProcessName()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setCodeReference(null);
|
||||
action.setProcessName(null);
|
||||
},
|
||||
"missing both");
|
||||
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setProcessName(TestUtils.PROCESS_NAME_GREET_PEOPLE);
|
||||
},
|
||||
"has both");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableAutomationActionFilter()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria())
|
||||
);
|
||||
},
|
||||
"without a field name", "without an operator");
|
||||
|
||||
assertValidationFailureReasons((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("notAField", QCriteriaOperator.EQUALS, Collections.emptyList()))
|
||||
);
|
||||
},
|
||||
"unrecognized field");
|
||||
|
||||
assertValidationSuccess((qInstance) ->
|
||||
{
|
||||
TableAutomationAction action = getAction0(qInstance);
|
||||
action.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("id", QCriteriaOperator.LESS_THAN_OR_EQUALS, List.of(1701)))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private TableAutomationAction getAction0(QInstance qInstance)
|
||||
{
|
||||
return qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getActions().get(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Run a little setup code on a qInstance; then validate it, and assert that it
|
||||
** failed validation with reasons that match the supplied vararg-reasons (but allow
|
||||
@ -690,7 +920,8 @@ class QInstanceValidatorTest
|
||||
if(!allowExtraReasons)
|
||||
{
|
||||
int noOfReasons = e.getReasons() == null ? 0 : e.getReasons().size();
|
||||
assertEquals(reasons.length, noOfReasons, "Expected number of validation failure reasons.\nExpected reasons: " + String.join(",", reasons) + "\nActual reasons: " + e.getReasons());
|
||||
assertEquals(reasons.length, noOfReasons, "Expected number of validation failure reasons.\nExpected reasons: " + String.join(",", reasons)
|
||||
+ "\nActual reasons: " + (noOfReasons > 0 ? String.join("\n", e.getReasons()) : "--"));
|
||||
}
|
||||
|
||||
for(String reason : reasons)
|
||||
|
@ -23,26 +23,39 @@ package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
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.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||
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.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.automation.RecordAutomationInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
|
||||
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.automation.PollingAutomationProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.automation.QAutomationProviderMetaData;
|
||||
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.QCodeUsage;
|
||||
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.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
@ -52,7 +65,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMet
|
||||
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.QRecordListMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTracking;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTrackingType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.MockAuthenticationModule;
|
||||
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
|
||||
@ -61,6 +79,8 @@ import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockB
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -69,6 +89,8 @@ import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackend
|
||||
*******************************************************************************/
|
||||
public class TestUtils
|
||||
{
|
||||
private static final Logger LOG = LogManager.getLogger(TestUtils.class);
|
||||
|
||||
public static final String DEFAULT_BACKEND_NAME = "default";
|
||||
public static final String MEMORY_BACKEND_NAME = "memory";
|
||||
|
||||
@ -83,11 +105,15 @@ public class TestUtils
|
||||
public static final String PROCESS_NAME_GREET_PEOPLE_INTERACTIVE = "greetInteractive";
|
||||
public static final String PROCESS_NAME_ADD_TO_PEOPLES_AGE = "addToPeoplesAge";
|
||||
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
||||
public static final String TABLE_NAME_PERSON_MEMORY = "personMemory";
|
||||
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
||||
|
||||
public static final String POSSIBLE_VALUE_SOURCE_STATE = "state"; // enum-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_SHAPE = "shape"; // table-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_CUSTOM = "custom"; // custom-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_STATE = "state"; // enum-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_SHAPE = "shape"; // table-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_CUSTOM = "custom"; // custom-type
|
||||
public static final String POSSIBLE_VALUE_SOURCE_AUTOMATION_STATUS = "automationStatus";
|
||||
|
||||
public static final String POLLING_AUTOMATION = "polling";
|
||||
|
||||
|
||||
|
||||
@ -104,9 +130,11 @@ public class TestUtils
|
||||
|
||||
qInstance.addTable(defineTablePerson());
|
||||
qInstance.addTable(definePersonFileTable());
|
||||
qInstance.addTable(definePersonMemoryTable());
|
||||
qInstance.addTable(defineTableIdAndNameOnly());
|
||||
qInstance.addTable(defineTableShape());
|
||||
|
||||
qInstance.addPossibleValueSource(defineAutomationStatusPossibleValueSource());
|
||||
qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
|
||||
qInstance.addPossibleValueSource(defineShapePossibleValueSource());
|
||||
qInstance.addPossibleValueSource(defineCustomPossibleValueSource());
|
||||
@ -117,6 +145,8 @@ public class TestUtils
|
||||
qInstance.addProcess(new BasicETLProcess().defineProcessMetaData());
|
||||
qInstance.addProcess(new StreamedETLProcess().defineProcessMetaData());
|
||||
|
||||
qInstance.addAutomationProvider(definePollingAutomationProvider());
|
||||
|
||||
defineApps(qInstance);
|
||||
|
||||
return (qInstance);
|
||||
@ -124,6 +154,18 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QAutomationProviderMetaData definePollingAutomationProvider()
|
||||
{
|
||||
return (new PollingAutomationProviderMetaData()
|
||||
.withName(POLLING_AUTOMATION)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -148,6 +190,21 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the "automationStatus" possible value source used in standard tests
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QPossibleValueSource defineAutomationStatusPossibleValueSource()
|
||||
{
|
||||
return new QPossibleValueSource()
|
||||
.withName(POSSIBLE_VALUE_SOURCE_AUTOMATION_STATUS)
|
||||
.withType(QPossibleValueSourceType.ENUM)
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
|
||||
.withValuesFromEnum(AutomationStatus.values());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the "states" possible value source used in standard tests
|
||||
**
|
||||
@ -252,6 +309,30 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QFieldMetaData standardQqqAutomationStatusField()
|
||||
{
|
||||
return (new QFieldMetaData("qqqAutomationStatus", QFieldType.INTEGER).withPossibleValueSourceName(POSSIBLE_VALUE_SOURCE_AUTOMATION_STATUS));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QTableAutomationDetails defineStandardAutomationDetails()
|
||||
{
|
||||
return (new QTableAutomationDetails()
|
||||
.withProviderName(POLLING_AUTOMATION)
|
||||
.withStatusTracking(new AutomationStatusTracking()
|
||||
.withType(AutomationStatusTrackingType.FIELD_IN_TABLE)
|
||||
.withFieldName("qqqAutomationStatus")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define the 'shape' table used in standard tests.
|
||||
*******************************************************************************/
|
||||
@ -289,6 +370,101 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define a 3nd version of the 'person' table, backed by the in-memory backend
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData definePersonMemoryTable()
|
||||
{
|
||||
return (new QTableMetaData()
|
||||
.withName(TABLE_NAME_PERSON_MEMORY)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withFields(TestUtils.defineTablePerson().getFields()))
|
||||
|
||||
.withField(standardQqqAutomationStatusField())
|
||||
.withAutomationDetails(defineStandardAutomationDetails()
|
||||
.withAction(new TableAutomationAction()
|
||||
.withName("checkAgeOnInsert")
|
||||
.withTriggerEvent(TriggerEvent.POST_INSERT)
|
||||
.withCodeReference(new QCodeReference(CheckAge.class))
|
||||
)
|
||||
.withAction(new TableAutomationAction()
|
||||
.withName("logOnUpdatePerFilter")
|
||||
.withTriggerEvent(TriggerEvent.POST_UPDATE)
|
||||
.withFilter(new QQueryFilter().withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Darin"))))
|
||||
.withCodeReference(new QCodeReference(LogPersonUpdate.class))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class CheckAge extends RecordAutomationHandler
|
||||
{
|
||||
public static String SUFFIX_FOR_MINORS = " (a minor)";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void execute(RecordAutomationInput recordAutomationInput) throws QException
|
||||
{
|
||||
LocalDate limitDate = LocalDate.now().minusYears(18);
|
||||
List<QRecord> recordsToUpdate = new ArrayList<>();
|
||||
for(QRecord record : recordAutomationInput.getRecordList())
|
||||
{
|
||||
LocalDate birthDate = record.getValueLocalDate("birthDate");
|
||||
if(birthDate != null && birthDate.isAfter(limitDate))
|
||||
{
|
||||
LOG.info("Person [" + record.getValueInteger("id") + "] is a minor - updating their firstName to state such.");
|
||||
recordsToUpdate.add(new QRecord()
|
||||
.withValue("id", record.getValue("id"))
|
||||
.withValue("firstName", record.getValueString("firstName") + SUFFIX_FOR_MINORS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(!recordsToUpdate.isEmpty())
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput(recordAutomationInput.getInstance());
|
||||
updateInput.setSession(recordAutomationInput.getSession());
|
||||
updateInput.setTableName(recordAutomationInput.getTableName());
|
||||
updateInput.setRecords(recordsToUpdate);
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class LogPersonUpdate extends RecordAutomationHandler
|
||||
{
|
||||
public static List<Integer> updatedIds = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void execute(RecordAutomationInput recordAutomationInput) throws QException
|
||||
{
|
||||
for(QRecord record : recordAutomationInput.getRecordList())
|
||||
{
|
||||
updatedIds.add(record.getValueInteger("id"));
|
||||
LOG.info("Person [" + record.getValueInteger("id") + ":" + record.getValueString("firstName") + "] has been updated.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define simple table with just an id and name
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user