mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
Initial version of abstract merge duplicates process
This commit is contained in:
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.mergeduplicates;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
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.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for MergeDuplicatesProcess
|
||||
*******************************************************************************/
|
||||
class MergeDuplicatesProcessTest extends BaseTest
|
||||
{
|
||||
String PROCESS_NAME = "testMergeProcess";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws Exception
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
addProcessToInstance();
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "Darin").withValue("noOfShoes", 1).withValue("favoriteShapeId", 11),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "Tim").withValue("noOfShoes", 2).withValue("favoriteShapeId", 12),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Tyler").withValue("noOfShoes", 1).withValue("favoriteShapeId", 13),
|
||||
new QRecord().withValue("id", 4).withValue("firstName", "Darin").withValue("noOfShoes", 1).withValue("favoriteShapeId", 14),
|
||||
new QRecord().withValue("id", 5).withValue("firstName", "Darin").withValue("noOfShoes", 1).withValue("favoriteShapeId", 15),
|
||||
new QRecord().withValue("id", 6).withValue("firstName", "James").withValue("noOfShoes", 1).withValue("favoriteShapeId", 16),
|
||||
new QRecord().withValue("id", 7).withValue("firstName", "James").withValue("noOfShoes", 1).withValue("favoriteShapeId", 17)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_SHAPE), List.of(
|
||||
new QRecord().withValue("id", 11).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 12).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 13).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 14).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 15).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 16).withValue("favoredByNoOfPeople", 1),
|
||||
new QRecord().withValue("id", 17).withValue("favoredByNoOfPeople", 1)
|
||||
));
|
||||
|
||||
RunProcessInput runProcessInput = new RunProcessInput();
|
||||
runProcessInput.setProcessName(PROCESS_NAME);
|
||||
runProcessInput.addValue("recordIds", "1,2,3,4,5,6,7");
|
||||
runProcessInput.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
|
||||
RunProcessAction runProcessAction = new RunProcessAction();
|
||||
RunProcessOutput runProcessOutput = runProcessAction.execute(runProcessInput);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<ProcessSummaryLineInterface> processResults = (ArrayList<ProcessSummaryLineInterface>) runProcessOutput.getValues().get("processResults");
|
||||
|
||||
assertThat(processResults.get(0))
|
||||
.hasFieldOrPropertyWithValue("message", "was updated")
|
||||
.hasFieldOrPropertyWithValue("count", 1);
|
||||
|
||||
assertThat(processResults.get(1))
|
||||
.hasFieldOrPropertyWithValue("message", "were deleted")
|
||||
.hasFieldOrPropertyWithValue("count", 2);
|
||||
|
||||
assertThat(processResults.get(2))
|
||||
.hasFieldOrPropertyWithValue("message", "did not have any duplicates.")
|
||||
.hasFieldOrPropertyWithValue("count", 2);
|
||||
|
||||
assertThat(processResults.get(3))
|
||||
.hasFieldOrPropertyWithValue("message", "were skipped, because it was not clear how they should have been processed.")
|
||||
.hasFieldOrPropertyWithValue("count", 2);
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// make sure records 4 and 5 have been deleted //
|
||||
/////////////////////////////////////////////////
|
||||
Map<Serializable, QRecord> personMap = GeneralProcessUtils.loadTableToMap(runProcessInput, TestUtils.TABLE_NAME_PERSON_MEMORY, "id");
|
||||
assertEquals(5, personMap.size());
|
||||
assertNull(personMap.get(4));
|
||||
assertNull(personMap.get(5));
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// make sure person 1's noOfShoes was updated //
|
||||
////////////////////////////////////////////////
|
||||
assertEquals(3, personMap.get(1).getValueInteger("noOfShoes"));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// make sure the shapes corresponding to records 4 and 5 have been deleted //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Map<Serializable, QRecord> shapesMap = GeneralProcessUtils.loadTableToMap(runProcessInput, TestUtils.TABLE_NAME_SHAPE, "id");
|
||||
assertEquals(5, shapesMap.size());
|
||||
assertNull(shapesMap.get(4));
|
||||
assertNull(shapesMap.get(5));
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// make sure the 'other table' got an update //
|
||||
///////////////////////////////////////////////
|
||||
assertEquals(3, shapesMap.get(11).getValueInteger("favoredByNoOfPeople"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void addProcessToInstance()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
qInstance.addProcess(MergeDuplicatesProcess.processMetaDataBuilder()
|
||||
.withName(PROCESS_NAME)
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withMergeDuplicatesTransformStepClass(PersonMergeDuplicatesStep.class)
|
||||
.getProcessMetaData());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class PersonMergeDuplicatesStep extends AbstractMergeDuplicatesTransformStep
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
protected MergeProcessConfig getMergeProcessConfig()
|
||||
{
|
||||
return (new MergeProcessConfig(TestUtils.TABLE_NAME_PERSON_MEMORY, List.of("firstName"), true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QRecord buildRecordToKeep(RunBackendStepInput runBackendStepInput, List<QRecord> duplicateRecords) throws SkipTheseRecordsException
|
||||
{
|
||||
/////////////////////////////////////
|
||||
// keep the one with the lowest id //
|
||||
// add the other one's shoes to it //
|
||||
/////////////////////////////////////
|
||||
QRecord recordToKeep = duplicateRecords.get(0);
|
||||
int totalNoOfShoes = 0;
|
||||
for(QRecord duplicateRecord : duplicateRecords)
|
||||
{
|
||||
totalNoOfShoes += duplicateRecord.getValueInteger("noOfShoes");
|
||||
if(duplicateRecord.getValueInteger("id") < recordToKeep.getValueInteger("id"))
|
||||
{
|
||||
recordToKeep = duplicateRecord;
|
||||
}
|
||||
|
||||
if(duplicateRecord.getValueString("firstName").equals("James"))
|
||||
{
|
||||
throw (new SkipTheseRecordsException("We don't want to mess with a James record..."));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// for ones that we aren't keeping, set to delete their favorite shapes //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
for(QRecord duplicateRecord : duplicateRecords)
|
||||
{
|
||||
if(duplicateRecord != recordToKeep)
|
||||
{
|
||||
addOtherTableIdsToDelete(TestUtils.TABLE_NAME_SHAPE, List.of(duplicateRecord.getValueInteger("favoriteShapeId")));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// for the one that we are keeping, update its favorite shape //
|
||||
////////////////////////////////////////////////////////////////
|
||||
addOtherTableRecordsToStore(TestUtils.TABLE_NAME_SHAPE, List.of(new QRecord()
|
||||
.withValue("id", recordToKeep.getValue("favoriteShapeId"))
|
||||
.withValue("favoredByNoOfPeople", duplicateRecords.size())
|
||||
));
|
||||
|
||||
recordToKeep.setValue("noOfShoes", totalNoOfShoes);
|
||||
|
||||
return (recordToKeep);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user