mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
Adding table-cacheOf concept; ability to add a child record from child-list widget
This commit is contained in:
@ -69,8 +69,9 @@ class ChildRecordListRendererTest
|
||||
void testParentRecordNotFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
RenderWidgetInput input = new RenderWidgetInput(qInstance);
|
||||
@ -92,8 +93,9 @@ class ChildRecordListRendererTest
|
||||
void testNoChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
@ -122,8 +124,9 @@ class ChildRecordListRendererTest
|
||||
void testChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
|
@ -117,8 +117,9 @@ class ParentWidgetRendererTest
|
||||
void testNoChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
@ -147,8 +148,9 @@ class ParentWidgetRendererTest
|
||||
void testChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
|
@ -103,8 +103,9 @@ class ProcessWidgetRendererTest
|
||||
void testNoChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
@ -133,8 +134,9 @@ class ProcessWidgetRendererTest
|
||||
void testChildRecordsFound() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.defineWidgetFromJoin(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items");
|
||||
QWidgetMetaData widget = ChildRecordListRenderer.widgetMetaDataBuilder(qInstance.getJoin("orderLineItem"))
|
||||
.withLabel("Line Items")
|
||||
.getWidgetMetaData();
|
||||
qInstance.addWidget(widget);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
|
@ -22,12 +22,28 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
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.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.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
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.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -37,6 +53,19 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
class GetActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
MemoryRecordStore.resetStatistics();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** At the core level, there isn't much that can be asserted, as it uses the
|
||||
** mock implementation - just confirming that all of the "wiring" works.
|
||||
@ -55,4 +84,164 @@ class GetActionTest
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getRecord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCache() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "George").withValue("lastName", "Washington").withValue("noOfShoes", 5),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Adams"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Thomas").withValue("lastName", "Jefferson")
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "John", "lastName", "McCain"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update the record in the source table - then re-get from cache table - shouldn't see new value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
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("noOfShoes", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// delete the cached record; re-get, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput(qInstance);
|
||||
deleteInput.setSession(new QSession());
|
||||
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "George")));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// update the source record; see that it isn't updated in cache. //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
{
|
||||
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("noOfShoes", 7)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput(qInstance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertEquals(7, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// should only be 1 cache record at this point //
|
||||
/////////////////////////////////////////////////
|
||||
assertEquals(1, TestUtils.queryTable(TestUtils.defineInstance(), TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE).size());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// delete the source record - it will still be in the cache though. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput(qInstance);
|
||||
deleteInput.setSession(new QSession());
|
||||
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and now it should go away //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput(qInstance);
|
||||
updateInput.setSession(new QSession());
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getInput = new GetInput(qInstance);
|
||||
getInput.setSession(new QSession());
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1180,8 +1180,7 @@ class QInstanceValidatorTest
|
||||
void testValidUniqueKeys()
|
||||
{
|
||||
assertValidationSuccess((qInstance) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withUniqueKey(new UniqueKey().withFieldName("id"))
|
||||
.withUniqueKey(new UniqueKey().withFieldName("firstName").withFieldName("lastName")));
|
||||
.withUniqueKey(new UniqueKey().withFieldName("id")));
|
||||
}
|
||||
|
||||
|
||||
|
@ -86,15 +86,19 @@ import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.ReportType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
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.metadata.tables.cache.CacheOf;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||
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;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockBackendModule;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
|
||||
@ -133,6 +137,7 @@ public class TestUtils
|
||||
public static final String PROCESS_NAME_RUN_SHAPES_PERSON_REPORT = "runShapesPersonReport";
|
||||
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
||||
public static final String TABLE_NAME_PERSON_MEMORY = "personMemory";
|
||||
public static final String TABLE_NAME_PERSON_MEMORY_CACHE = "personMemoryCache";
|
||||
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
||||
public static final String TABLE_NAME_BASEPULL = "basepullTest";
|
||||
public static final String REPORT_NAME_SHAPES_PERSON = "shapesPersonReport";
|
||||
@ -164,6 +169,7 @@ public class TestUtils
|
||||
qInstance.addTable(defineTablePerson());
|
||||
qInstance.addTable(definePersonFileTable());
|
||||
qInstance.addTable(definePersonMemoryTable());
|
||||
qInstance.addTable(definePersonMemoryCacheTable());
|
||||
qInstance.addTable(defineTableIdAndNameOnly());
|
||||
qInstance.addTable(defineTableShape());
|
||||
qInstance.addTable(defineTableBasepull());
|
||||
@ -608,6 +614,7 @@ public class TestUtils
|
||||
.withName(TABLE_NAME_PERSON_MEMORY)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("firstName", "lastName"))
|
||||
.withFields(TestUtils.defineTablePerson().getFields()))
|
||||
|
||||
.withField(standardQqqAutomationStatusField())
|
||||
@ -635,6 +642,36 @@ public class TestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define yet another version of the 'person' table, also in-memory, and as a
|
||||
** cache on the other in-memory one...
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData definePersonMemoryCacheTable()
|
||||
{
|
||||
UniqueKey uniqueKey = new UniqueKey("firstName", "lastName");
|
||||
return (new QTableMetaData()
|
||||
.withName(TABLE_NAME_PERSON_MEMORY_CACHE)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withBackendDetails(new MemoryTableBackendDetails()
|
||||
.withCloneUponStore(true))
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(uniqueKey)
|
||||
.withFields(TestUtils.defineTablePerson().getFields()))
|
||||
.withField(new QFieldMetaData("cachedDate", QFieldType.DATE_TIME))
|
||||
.withCacheOf(new CacheOf()
|
||||
.withSourceTable(TABLE_NAME_PERSON_MEMORY)
|
||||
.withCachedDateFieldName("cachedDate")
|
||||
.withExpirationSeconds(60)
|
||||
.withUseCase(new CacheUseCase()
|
||||
.withType(CacheUseCase.Type.UNIQUE_KEY_TO_UNIQUE_KEY)
|
||||
.withSourceUniqueKey(uniqueKey)
|
||||
.withCacheUniqueKey(uniqueKey)
|
||||
.withCacheSourceMisses(false)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
Reference in New Issue
Block a user