Feedback from code reviews

This commit is contained in:
2022-09-05 09:47:43 -05:00
parent 4af7757fdd
commit 9a8b49f1a7
65 changed files with 1337 additions and 223 deletions

View File

@ -1,3 +1,24 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.automation.polling;
@ -10,11 +31,8 @@ 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.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.automation.RecordAutomationInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
@ -76,19 +94,16 @@ class PollingAutomationExecutorTest
/////////////////////////////////////////////////
// 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());
List<QRecord> records = TestUtils.queryTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
assertEquals(2, records.size());
Optional<QRecord> optionalPerson1 = queryOutput.getRecords().stream().filter(r -> r.getValueInteger("id") == 1).findFirst();
Optional<QRecord> optionalPerson1 = records.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();
Optional<QRecord> optionalPerson2 = records.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);

View File

@ -1,3 +1,24 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.automation.polling;
@ -14,8 +35,20 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
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.processes.QProcessMetaData;
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.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.backend.implementations.memory.MemoryRecordStore;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcessTest;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -51,15 +84,15 @@ class PollingAutomationRunnerTest
QInstance qInstance = TestUtils.defineInstance();
PollingAutomationRunner pollingAutomationRunner = new PollingAutomationRunner(qInstance, TestUtils.POLLING_AUTOMATION, null);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// insert 2 person records, one who should be both updated by the insert action, and should be logged by logger-on-update automation //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// insert 2 person records, both updated by the insert action, and 1 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").withValue("birthDate", LocalDate.now()),
new QRecord().withValue("id", 2).withValue("firstName", "Darin")
new QRecord().withValue("id", 2).withValue("firstName", "Darin").withValue("birthDate", LocalDate.now())
));
new InsertAction().execute(insertInput);
assertAllRecordsAutomationStatus(AutomationStatus.PENDING_INSERT_AUTOMATIONS);
@ -72,15 +105,11 @@ class PollingAutomationRunnerTest
assertThat(TestUtils.LogPersonUpdate.updatedIds).isNullOrEmpty();
assertAllRecordsAutomationStatus(AutomationStatus.OK);
////////////////////////////////////////////
// make sure the minor person was updated //
////////////////////////////////////////////
Optional<QRecord> updatedMinorRecord = TestUtils.queryTable(TestUtils.TABLE_NAME_PERSON_MEMORY).stream().filter(r -> r.getValueInteger("id").equals(1)).findFirst();
assertThat(updatedMinorRecord)
.isPresent()
.get()
.extracting(r -> r.getValueString("firstName"))
.isEqualTo("Tim" + TestUtils.CheckAge.SUFFIX_FOR_MINORS);
/////////////////////////////////////////
// make sure both persons were updated //
/////////////////////////////////////////
assertThat(TestUtils.queryTable(TestUtils.TABLE_NAME_PERSON_MEMORY))
.allMatch(r -> r.getValueString("firstName").endsWith(TestUtils.CheckAge.SUFFIX_FOR_MINORS));
/////////////////////////////////////////////////////////////////////////////////////////
// run automations again - make sure that there haven't been any updates triggered yet //
@ -179,7 +208,7 @@ class PollingAutomationRunnerTest
/*******************************************************************************
** Test a cycle that does an insert, some automations, then and an update, and more automations.
** Test running a process for automation, instead of a code ref.
*******************************************************************************/
@Test
void testRunningProcess() throws QException
@ -187,9 +216,9 @@ class PollingAutomationRunnerTest
QInstance qInstance = TestUtils.defineInstance();
PollingAutomationRunner pollingAutomationRunner = new PollingAutomationRunner(qInstance, TestUtils.POLLING_AUTOMATION, null);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// insert 2 person records, one who should be both updated by the insert action, and should be logged by logger-on-update automation //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// insert 2 person records, 1 to trigger the "increaseAge" action //
////////////////////////////////////////////////////////////////////
InsertInput insertInput = new InsertInput(qInstance);
insertInput.setSession(new QSession());
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
@ -214,6 +243,62 @@ class PollingAutomationRunnerTest
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRunningEtlWithFrontendProcess() throws QException
{
QInstance instance = TestUtils.defineInstance();
////////////////////////////////////////////////////////
// define the process - an ELT from Shapes to Persons //
////////////////////////////////////////////////////////
QProcessMetaData process = StreamedETLWithFrontendProcess.defineProcessMetaData(
TestUtils.TABLE_NAME_SHAPE,
TestUtils.TABLE_NAME_PERSON,
ExtractViaQueryStep.class,
StreamedETLWithFrontendProcessTest.TestTransformShapeToPersonStep.class,
LoadViaInsertStep.class);
process.setName("shapeToPersonETLProcess");
process.setTableName(TestUtils.TABLE_NAME_SHAPE);
instance.addProcess(process);
///////////////////////////////////////////////////////
// switch the person table to use the memory backend //
///////////////////////////////////////////////////////
instance.getTable(TestUtils.TABLE_NAME_PERSON).setBackendName(TestUtils.MEMORY_BACKEND_NAME);
///////////////////////////////////////////////////////////////////////
// add a post-insert process to the shape table, to run this ELT job //
///////////////////////////////////////////////////////////////////////
instance.getTable(TestUtils.TABLE_NAME_SHAPE)
.withField(new QFieldMetaData("automationStatus", QFieldType.INTEGER))
.setAutomationDetails(new QTableAutomationDetails()
.withProviderName(TestUtils.POLLING_AUTOMATION)
.withStatusTracking(new AutomationStatusTracking().withType(AutomationStatusTrackingType.FIELD_IN_TABLE).withFieldName("automationStatus"))
.withAction(new TableAutomationAction()
.withName("shapeToPerson")
.withTriggerEvent(TriggerEvent.POST_INSERT)
.withProcessName("shapeToPersonETLProcess")
)
);
TestUtils.insertDefaultShapes(instance);
PollingAutomationRunner pollingAutomationRunner = new PollingAutomationRunner(instance, TestUtils.POLLING_AUTOMATION, null);
pollingAutomationRunner.run();
List<QRecord> postList = TestUtils.queryTable(instance, TestUtils.TABLE_NAME_PERSON);
assertThat(postList)
.as("Should have inserted Circle").anyMatch(qr -> qr.getValue("lastName").equals("Circle"))
.as("Should have inserted Triangle").anyMatch(qr -> qr.getValue("lastName").equals("Triangle"))
.as("Should have inserted Square").anyMatch(qr -> qr.getValue("lastName").equals("Square"));
}
/*******************************************************************************
**
*******************************************************************************/

View File

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

View File

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

View File

@ -372,9 +372,9 @@ public class RunProcessTest
))
);
/////////////////////////////////////////////////////////////////////////////
// make sure that if we run by default, we get stop on both frontend steps //
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// make sure that if we run by default, we get stopped on both frontend steps //
////////////////////////////////////////////////////////////////////////////////
RunProcessInput request = new RunProcessInput(qInstance);
request.setSession(TestUtils.getMockSession());
request.setProcessName(processName);

View File

@ -721,9 +721,8 @@ class QInstanceValidatorTest
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?? //
//////////////////////////////////////////////////
assertValidationFailureReasons((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().getStatusTracking().setFieldName("notARealField"),
"not a defined field");
}
@ -791,14 +790,6 @@ class QInstanceValidatorTest
},
"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);
@ -826,9 +817,9 @@ class QInstanceValidatorTest
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);
action.setCodeReference(new QCodeReference(TestUtils.CheckAge.class));
action.setProcessName(TestUtils.PROCESS_NAME_INCREASE_BIRTHDATE);
},
"has both");
}
@ -857,14 +848,6 @@ class QInstanceValidatorTest
);
},
"unrecognized field");
assertValidationSuccess((qInstance) ->
{
TableAutomationAction action = getAction0(qInstance);
action.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria("id", QCriteriaOperator.LESS_THAN_OR_EQUALS, List.of(1701)))
);
});
}

View File

@ -1,3 +1,24 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend;
@ -36,7 +57,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for StreamedETLWithFrontendProcess
*******************************************************************************/
class StreamedETLWithFrontendProcessTest
public class StreamedETLWithFrontendProcessTest
{
/*******************************************************************************
@ -64,7 +85,7 @@ class StreamedETLWithFrontendProcessTest
////////////////////////////////////////////////////////
QProcessMetaData process = StreamedETLWithFrontendProcess.defineProcessMetaData(
TestUtils.TABLE_NAME_SHAPE,
TestUtils.TABLE_NAME_PERSON,
TestUtils.TABLE_NAME_PERSON_MEMORY,
ExtractViaQueryStep.class,
TestTransformShapeToPersonStep.class,
LoadViaInsertStep.class);
@ -72,11 +93,6 @@ class StreamedETLWithFrontendProcessTest
process.setTableName(TestUtils.TABLE_NAME_SHAPE);
instance.addProcess(process);
///////////////////////////////////////////////////////
// switch the person table to use the memory backend //
///////////////////////////////////////////////////////
instance.getTable(TestUtils.TABLE_NAME_PERSON).setBackendName(TestUtils.MEMORY_BACKEND_NAME);
TestUtils.insertDefaultShapes(instance);
/////////////////////
@ -84,7 +100,7 @@ class StreamedETLWithFrontendProcessTest
/////////////////////
runProcess(instance, process);
List<QRecord> postList = TestUtils.queryTable(instance, TestUtils.TABLE_NAME_PERSON);
List<QRecord> postList = TestUtils.queryTable(instance, TestUtils.TABLE_NAME_PERSON_MEMORY);
assertThat(postList)
.as("Should have inserted Circle").anyMatch(qr -> qr.getValue("lastName").equals("Circle"))
.as("Should have inserted Triangle").anyMatch(qr -> qr.getValue("lastName").equals("Triangle"))
@ -277,7 +293,7 @@ class StreamedETLWithFrontendProcessTest
/*******************************************************************************
**
*******************************************************************************/
private RunProcessOutput runProcess(QInstance instance, QProcessMetaData process) throws QException
public RunProcessOutput runProcess(QInstance instance, QProcessMetaData process) throws QException
{
return (runProcess(instance, process, new HashMap<>(), new Callback()));
}
@ -338,6 +354,14 @@ class StreamedETLWithFrontendProcessTest
getOutputRecordPage().add(newQRecord);
}
}
@Override
public ArrayList<ProcessSummaryLine> getProcessSummary(boolean isForResultScreen)
{
return null;
}
}
@ -420,6 +444,14 @@ class StreamedETLWithFrontendProcessTest
getOutputRecordPage().add(updatedQRecord);
}
}
@Override
public ArrayList<ProcessSummaryLine> getProcessSummary(boolean isForResultScreen)
{
return null;
}
}