Merge branch 'feature/QQQ-40-record-automations' into feature/sprint-10

This commit is contained in:
2022-08-31 15:12:54 -05:00
26 changed files with 2605 additions and 192 deletions

View File

@ -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();
}
}

View File

@ -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)

View File

@ -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
*******************************************************************************/