mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
Adding unique key check to insert action; adding post-insert customizer
This commit is contained in:
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.customizers;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
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.insert.InsertOutput;
|
||||
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.metadata.code.QCodeUsage;
|
||||
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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ChildInserterPostInsertCustomizer
|
||||
**
|
||||
** We'll use person & shape tables here, w/ the favoriteShapeId foreign key,
|
||||
** so a rule of "every time we insert a person, if they aren't already pointed at
|
||||
** a favoriteShape, insert a new shape for them".
|
||||
*******************************************************************************/
|
||||
class ChildInserterPostInsertCustomizerTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testEmptyCases() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of());
|
||||
|
||||
////////////////////////////////////////
|
||||
// just looking for no exception here //
|
||||
////////////////////////////////////////
|
||||
new InsertAction().execute(insertInput);
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now insert one person, but they shouldn't need a favoriteShape to be inserted - again, make sure we don't blow up and that no shapes get inserted. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("firstName", "James").withValue("favoriteShapeId", -1)));
|
||||
new InsertAction().execute(insertInput);
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void addPostInsertActionToTable(QInstance qInstance)
|
||||
{
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withCustomizer(TableCustomizers.POST_INSERT_RECORD.getTableCustomizer(), new QCodeReference(PersonPostInsertAddFavoriteShapeCustomizer.class, QCodeUsage.CUSTOMIZER));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSimpleCase() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
Serializable favoriteShapeId = insertOutput.getRecords().get(0).getValue("favoriteShapeId");
|
||||
assertNotNull(favoriteShapeId);
|
||||
|
||||
List<QRecord> shapeRecords = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE);
|
||||
assertEquals(1, shapeRecords.size());
|
||||
assertEquals(favoriteShapeId, shapeRecords.get(0).getValue("id"));
|
||||
assertEquals("Darin's favorite shape!", shapeRecords.get(0).getValue("name"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testComplexCase() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin"),
|
||||
new QRecord().withValue("firstName", "James").withValue("favoriteShapeId", -1),
|
||||
new QRecord().withValue("firstName", "Tim"),
|
||||
new QRecord().withValue("firstName", "Garret").withValue("favoriteShapeId", -2)
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(1, insertOutput.getRecords().get(0).getValue("favoriteShapeId"));
|
||||
assertEquals(-1, insertOutput.getRecords().get(1).getValue("favoriteShapeId"));
|
||||
assertEquals(2, insertOutput.getRecords().get(2).getValue("favoriteShapeId"));
|
||||
assertEquals(-2, insertOutput.getRecords().get(3).getValue("favoriteShapeId"));
|
||||
|
||||
List<QRecord> shapeRecords = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE);
|
||||
assertEquals(2, shapeRecords.size());
|
||||
assertEquals(1, shapeRecords.get(0).getValue("id"));
|
||||
assertEquals(2, shapeRecords.get(1).getValue("id"));
|
||||
assertEquals("Darin's favorite shape!", shapeRecords.get(0).getValue("name"));
|
||||
assertEquals("Tim's favorite shape!", shapeRecords.get(1).getValue("name"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class PersonPostInsertAddFavoriteShapeCustomizer extends ChildInserterPostInsertCustomizer
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QRecord buildChildForRecord(QRecord parentRecord) throws QException
|
||||
{
|
||||
return (new QRecord().withValue("name", parentRecord.getValue("firstName") + "'s favorite shape!"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getChildTableName()
|
||||
{
|
||||
return (TestUtils.TABLE_NAME_SHAPE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getForeignKeyFieldName()
|
||||
{
|
||||
return ("favoriteShapeId");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public RelationshipType getRelationshipType()
|
||||
{
|
||||
return (RelationshipType.PARENT_POINTS_AT_CHILD);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -28,9 +28,20 @@ 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.insert.InsertOutput;
|
||||
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.tables.UniqueKey;
|
||||
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.CollectionUtils;
|
||||
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;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -40,6 +51,18 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
class InsertActionTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void beforeAndAfterEach()
|
||||
{
|
||||
MemoryRecordStore.getInstance().reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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.
|
||||
@ -51,8 +74,8 @@ class InsertActionTest
|
||||
InsertInput request = new InsertInput(TestUtils.defineInstance());
|
||||
request.setSession(TestUtils.getMockSession());
|
||||
request.setTableName("person");
|
||||
List<QRecord> records =new ArrayList<>();
|
||||
QRecord record = new QRecord();
|
||||
List<QRecord> records = new ArrayList<>();
|
||||
QRecord record = new QRecord();
|
||||
record.setValue("firstName", "James");
|
||||
records.add(record);
|
||||
request.setRecords(records);
|
||||
@ -60,4 +83,134 @@ class InsertActionTest
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeysPreExisting() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(1, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// try to insert that person again - shouldn't work. //
|
||||
///////////////////////////////////////////////////////
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")
|
||||
));
|
||||
insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(1, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
assertNull(insertOutput.getRecords().get(0).getValueInteger("id"));
|
||||
assertEquals(1, insertOutput.getRecords().get(0).getErrors().size());
|
||||
assertThat(insertOutput.getRecords().get(0).getErrors().get(0)).contains("Another record already exists with this First Name and Last Name");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to insert that person again, with 2 others - the 2 should work, but the one fail //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Smith"),
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff"),
|
||||
new QRecord().withValue("firstName", "Trevor").withValue("lastName", "Kelkhoff")
|
||||
));
|
||||
insertOutput = new InsertAction().execute(insertInput);
|
||||
assertEquals(3, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
assertNotNull(insertOutput.getRecords().get(0).getValueInteger("id"));
|
||||
assertNull(insertOutput.getRecords().get(1).getValueInteger("id"));
|
||||
assertNotNull(insertOutput.getRecords().get(2).getValueInteger("id"));
|
||||
assertEquals(0, insertOutput.getRecords().get(0).getErrors().size());
|
||||
assertEquals(1, insertOutput.getRecords().get(1).getErrors().size());
|
||||
assertEquals(0, insertOutput.getRecords().get(2).getErrors().size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeysWithinBatch() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff"),
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
assertEquals(1, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
assertEquals(1, insertOutput.getRecords().get(0).getValueInteger("id"));
|
||||
assertNull(insertOutput.getRecords().get(1).getValueInteger("id"));
|
||||
assertEquals(1, insertOutput.getRecords().get(1).getErrors().size());
|
||||
assertThat(insertOutput.getRecords().get(1).getErrors().get(0)).contains("Another record already exists with this First Name and Last Name");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSingleColumnUniqueKey() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_SHAPE)
|
||||
.withUniqueKey(new UniqueKey("name"));
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_SHAPE);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("name", "Circle"),
|
||||
new QRecord().withValue("name", "Circle")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
assertEquals(1, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
assertEquals(1, insertOutput.getRecords().get(0).getValueInteger("id"));
|
||||
assertNull(insertOutput.getRecords().get(1).getValueInteger("id"));
|
||||
assertEquals(1, insertOutput.getRecords().get(1).getErrors().size());
|
||||
assertThat(insertOutput.getRecords().get(1).getErrors().get(0)).contains("Another record already exists with this Name");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSkippingUniqueKeys() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
InsertInput insertInput = new InsertInput(qInstance);
|
||||
insertInput.setSession(new QSession());
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
insertInput.setSkipUniqueKeyCheck(true);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff"),
|
||||
new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
assertEquals(2, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
assertEquals(1, insertOutput.getRecords().get(0).getValueInteger("id"));
|
||||
assertEquals(2, insertOutput.getRecords().get(1).getValueInteger("id"));
|
||||
assertTrue(CollectionUtils.nullSafeIsEmpty(insertOutput.getRecords().get(1).getErrors()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -98,7 +98,6 @@ 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;
|
||||
@ -652,8 +651,6 @@ public class TestUtils
|
||||
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()))
|
||||
|
Reference in New Issue
Block a user