mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
Merge branch 'feature/CTLE-207-query-joins' into integration/sprint-25
# Conflicts: # qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java # qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandler.java
This commit is contained in:
@ -22,10 +22,12 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.reporting;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
@ -36,16 +38,21 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||
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.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -117,6 +124,68 @@ class ExportActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testJoins() throws QException, IOException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QContext.getQSession().withSecurityKeyValue(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS, true);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_ORDER), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("orderNo", "ORD1").withValue("storeId", 1),
|
||||
new QRecord().withValue("id", 2).withValue("orderNo", "ORD2").withValue("storeId", 1)
|
||||
));
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_LINE_ITEM), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("orderId", 1).withValue("sku", "A").withValue("quantity", 10),
|
||||
new QRecord().withValue("id", 2).withValue("orderId", 1).withValue("sku", "B").withValue("quantity", 15),
|
||||
new QRecord().withValue("id", 3).withValue("orderId", 2).withValue("sku", "A").withValue("quantity", 20)
|
||||
));
|
||||
|
||||
ExportInput exportInput = new ExportInput();
|
||||
exportInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
exportInput.setReportFormat(ReportFormat.CSV);
|
||||
ByteArrayOutputStream reportOutputStream = new ByteArrayOutputStream();
|
||||
exportInput.setReportOutputStream(reportOutputStream);
|
||||
exportInput.setQueryFilter(new QQueryFilter());
|
||||
exportInput.setFieldNames(List.of("id", "orderNo", "storeId", "orderLine.id", "orderLine.sku", "orderLine.quantity"));
|
||||
// exportInput.setFieldNames(List.of("id", "orderNo", "storeId"));
|
||||
new ExportAction().execute(exportInput);
|
||||
|
||||
String csv = reportOutputStream.toString(StandardCharsets.UTF_8);
|
||||
CSVParser parse = CSVParser.parse(csv, CSVFormat.DEFAULT.withFirstRecordAsHeader());
|
||||
Iterator<CSVRecord> csvRecordIterator = parse.iterator();
|
||||
assertFalse(parse.getHeaderMap().isEmpty());
|
||||
assertTrue(parse.getHeaderMap().containsKey("Id"));
|
||||
assertTrue(parse.getHeaderMap().containsKey("Order Line: Id"));
|
||||
assertTrue(parse.getHeaderMap().containsKey("Order Line: SKU"));
|
||||
|
||||
CSVRecord csvRecord = csvRecordIterator.next();
|
||||
assertEquals("1", csvRecord.get("Id"));
|
||||
assertEquals("1", csvRecord.get("Order Line: Id"));
|
||||
assertEquals("A", csvRecord.get("Order Line: SKU"));
|
||||
assertEquals("10", csvRecord.get("Order Line: Quantity"));
|
||||
|
||||
csvRecord = csvRecordIterator.next();
|
||||
assertEquals("1", csvRecord.get("Id"));
|
||||
assertEquals("2", csvRecord.get("Order Line: Id"));
|
||||
assertEquals("B", csvRecord.get("Order Line: SKU"));
|
||||
assertEquals("15", csvRecord.get("Order Line: Quantity"));
|
||||
|
||||
csvRecord = csvRecordIterator.next();
|
||||
assertEquals("2", csvRecord.get("Id"));
|
||||
assertEquals("3", csvRecord.get("Order Line: Id"));
|
||||
assertEquals("A", csvRecord.get("Order Line: SKU"));
|
||||
assertEquals("20", csvRecord.get("Order Line: Quantity"));
|
||||
|
||||
assertFalse(csvRecordIterator.hasNext());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -32,7 +32,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
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.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||
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.Tier;
|
||||
@ -41,6 +45,7 @@ import org.junit.jupiter.api.Test;
|
||||
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_GREETINGS;
|
||||
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_MISCELLANEOUS;
|
||||
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_PEOPLE;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
@ -304,4 +309,131 @@ class QInstanceEnricherTest extends BaseTest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testExposedJoinPaths()
|
||||
{
|
||||
//////////////////////////
|
||||
// no join path => fail //
|
||||
//////////////////////////
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B")));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
assertThatThrownBy(() -> new QInstanceEnricher(qInstance).enrich())
|
||||
.rootCause()
|
||||
.hasMessageContaining("Could not infer a joinPath for table [A], exposedJoin to [B]")
|
||||
.hasMessageContaining("No join connections between these tables exist in this instance.");
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
// multiple join paths => fail //
|
||||
/////////////////////////////////
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B")));
|
||||
qInstance.addTable(newTable("B", "id", "aId1", "aId2"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB1").withJoinOn(new JoinOn("id", "aId1")).withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB2").withJoinOn(new JoinOn("id", "aId2")).withType(JoinType.ONE_TO_ONE));
|
||||
assertThatThrownBy(() -> new QInstanceEnricher(qInstance).enrich())
|
||||
.rootCause()
|
||||
.hasMessageContaining("Could not infer a joinPath for table [A], exposedJoin to [B]")
|
||||
.hasMessageContaining("2 join connections exist")
|
||||
.hasMessageContaining("\nAB1\n")
|
||||
.hasMessageContaining("\nAB2.");
|
||||
|
||||
////////////////////////////////////////////
|
||||
// but if you specify a path, you're good //
|
||||
////////////////////////////////////////////
|
||||
qInstance.getTable("A").getExposedJoins().get(0).setJoinPath(List.of("AB2"));
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
assertEquals("B", qInstance.getTable("A").getExposedJoins().get(0).getLabel());
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
// multiple join paths => fail //
|
||||
/////////////////////////////////
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("C")));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addTable(newTable("C", "id", "bId", "aId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withJoinOn(new JoinOn("id", "aId")).withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("B").withRightTable("C").withName("BC").withJoinOn(new JoinOn("id", "bId")).withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("C").withName("AC").withJoinOn(new JoinOn("id", "aId")).withType(JoinType.ONE_TO_ONE));
|
||||
assertThatThrownBy(() -> new QInstanceEnricher(qInstance).enrich())
|
||||
.rootCause()
|
||||
.hasMessageContaining("Could not infer a joinPath for table [A], exposedJoin to [C]")
|
||||
.hasMessageContaining("2 join connections exist")
|
||||
.hasMessageContaining("\nAB, BC\n")
|
||||
.hasMessageContaining("\nAC.");
|
||||
|
||||
////////////////////////////////////////////
|
||||
// but if you specify a path, you're good //
|
||||
////////////////////////////////////////////
|
||||
qInstance.getTable("A").getExposedJoins().get(0).setJoinPath(List.of("AB", "BC"));
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
assertEquals("C", qInstance.getTable("A").getExposedJoins().get(0).getLabel());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// even if you specify a bogus path, Enricher doesn't care - see validator to care. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B").withJoinPath(List.of("not-a-join"))));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// one join path => great success //
|
||||
////////////////////////////////////
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id")
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("B"))
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("C")));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addTable(newTable("C", "id", "bId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withJoinOn(new JoinOn("id", "aId")).withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("B").withRightTable("C").withName("BC").withJoinOn(new JoinOn("id", "bId")).withType(JoinType.ONE_TO_ONE));
|
||||
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
|
||||
ExposedJoin exposedJoinAB = qInstance.getTable("A").getExposedJoins().get(0);
|
||||
assertEquals("B", exposedJoinAB.getLabel());
|
||||
assertEquals(List.of("AB"), exposedJoinAB.getJoinPath());
|
||||
|
||||
ExposedJoin exposedJoinAC = qInstance.getTable("A").getExposedJoins().get(1);
|
||||
assertEquals("C", exposedJoinAC.getLabel());
|
||||
assertEquals(List.of("AB", "BC"), exposedJoinAC.getJoinPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData newTable(String tableName, String... fieldNames)
|
||||
{
|
||||
QTableMetaData tableMetaData = new QTableMetaData()
|
||||
.withName(tableName)
|
||||
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
|
||||
.withPrimaryKeyField(fieldNames[0]);
|
||||
|
||||
for(String fieldName : fieldNames)
|
||||
{
|
||||
tableMetaData.addField(new QFieldMetaData(fieldName, QFieldType.INTEGER));
|
||||
}
|
||||
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ 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.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
@ -65,6 +68,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||
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.Tier;
|
||||
@ -1671,6 +1675,83 @@ class QInstanceValidatorTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testExposedJoinPaths()
|
||||
{
|
||||
assertValidationFailureReasons(qInstance -> qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin())),
|
||||
"Table A has an exposedJoin that is missing a joinTable name",
|
||||
"Table A exposedJoin [missingJoinTableName] is missing a label");
|
||||
|
||||
assertValidationFailureReasons(qInstance -> qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B"))),
|
||||
"Table A exposedJoin B is referencing an unrecognized table",
|
||||
"Table A exposedJoin B is missing a label");
|
||||
|
||||
assertValidationFailureReasons(qInstance ->
|
||||
{
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B").withLabel("B").withJoinPath(List.of("notAJoin"))));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId")));
|
||||
},
|
||||
"does not match a valid join connection in the instance");
|
||||
|
||||
assertValidationFailureReasons(qInstance ->
|
||||
{
|
||||
qInstance.addTable(newTable("A", "id")
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("B").withLabel("foo").withJoinPath(List.of("AB")))
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("C").withLabel("foo").withJoinPath(List.of("AC")))
|
||||
);
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addTable(newTable("C", "id", "aId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId")));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("C").withName("AC").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId")));
|
||||
},
|
||||
"more than one join labeled: foo");
|
||||
|
||||
assertValidationFailureReasons(qInstance ->
|
||||
{
|
||||
qInstance.addTable(newTable("A", "id")
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("B").withLabel("B1").withJoinPath(List.of("AB")))
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable("B").withLabel("B2").withJoinPath(List.of("AB")))
|
||||
);
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId")));
|
||||
},
|
||||
"than one join with the joinPath: [AB]");
|
||||
|
||||
assertValidationSuccess(qInstance ->
|
||||
{
|
||||
qInstance.addTable(newTable("A", "id").withExposedJoin(new ExposedJoin().withJoinTable("B").withLabel("B").withJoinPath(List.of("AB"))));
|
||||
qInstance.addTable(newTable("B", "id", "aId"));
|
||||
qInstance.addJoin(new QJoinMetaData().withLeftTable("A").withRightTable("B").withName("AB").withType(JoinType.ONE_TO_ONE).withJoinOn(new JoinOn("id", "aId")));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData newTable(String tableName, String... fieldNames)
|
||||
{
|
||||
QTableMetaData tableMetaData = new QTableMetaData()
|
||||
.withName(tableName)
|
||||
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
|
||||
.withPrimaryKeyField(fieldNames[0]);
|
||||
|
||||
for(String fieldName : fieldNames)
|
||||
{
|
||||
tableMetaData.addField(new QFieldMetaData(fieldName, QFieldType.INTEGER));
|
||||
}
|
||||
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.model.metadata.tables;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ExposedJoin
|
||||
*******************************************************************************/
|
||||
class ExposedJoinTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIsManyOneToOne()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData().withName("A").withExposedJoin(new ExposedJoin().withJoinTable("B").withJoinPath(List.of("AB"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("B").withExposedJoin(new ExposedJoin().withJoinTable("A").withJoinPath(List.of("AB"))));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("AB").withLeftTable("A").withRightTable("B").withType(JoinType.ONE_TO_ONE));
|
||||
|
||||
assertFalse(qInstance.getTable("A").getExposedJoins().get(0).getIsMany());
|
||||
assertFalse(qInstance.getTable("B").getExposedJoins().get(0).getIsMany());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIsManyOneToMany()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData().withName("A").withExposedJoin(new ExposedJoin().withJoinTable("B").withJoinPath(List.of("AB"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("B").withExposedJoin(new ExposedJoin().withJoinTable("A").withJoinPath(List.of("AB"))));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("AB").withLeftTable("A").withRightTable("B").withType(JoinType.ONE_TO_MANY));
|
||||
|
||||
assertTrue(qInstance.getTable("A").getExposedJoins().get(0).getIsMany());
|
||||
assertFalse(qInstance.getTable("B").getExposedJoins().get(0).getIsMany());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIsManyManyToOne()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData().withName("A").withExposedJoin(new ExposedJoin().withJoinTable("B").withJoinPath(List.of("AB"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("B").withExposedJoin(new ExposedJoin().withJoinTable("A").withJoinPath(List.of("AB"))));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("AB").withLeftTable("A").withRightTable("B").withType(JoinType.MANY_TO_ONE));
|
||||
|
||||
assertFalse(qInstance.getTable("A").getExposedJoins().get(0).getIsMany());
|
||||
assertTrue(qInstance.getTable("B").getExposedJoins().get(0).getIsMany());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIsManyOneToOneThroughChain()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData().withName("A").withExposedJoin(new ExposedJoin().withJoinTable("G").withJoinPath(List.of("AB", "BC", "CD", "DE", "EF", "FG"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("B").withExposedJoin(new ExposedJoin().withJoinTable("G").withJoinPath(List.of("BC", "CD", "DE", "EF", "FG"))));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("AB").withLeftTable("A").withRightTable("B").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("BC").withLeftTable("B").withRightTable("C").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("CD").withLeftTable("C").withRightTable("D").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("DE").withLeftTable("D").withRightTable("E").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("EF").withLeftTable("E").withRightTable("F").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("FG").withLeftTable("F").withRightTable("G").withType(JoinType.ONE_TO_ONE));
|
||||
|
||||
assertFalse(qInstance.getTable("A").getExposedJoins().get(0).getIsMany());
|
||||
assertFalse(qInstance.getTable("B").getExposedJoins().get(0).getIsMany());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIsManyOneToManyThroughChain()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addTable(new QTableMetaData().withName("A").withExposedJoin(new ExposedJoin().withJoinTable("G").withJoinPath(List.of("AB", "BC", "CD", "DE", "EF", "FG"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("B").withExposedJoin(new ExposedJoin().withJoinTable("E").withJoinPath(List.of("BC", "CD", "DE"))));
|
||||
qInstance.addTable(new QTableMetaData().withName("F").withExposedJoin(new ExposedJoin().withJoinTable("C").withJoinPath(List.of("FG", "EF", "DE", "CD"))));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("AB").withLeftTable("A").withRightTable("B").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("BC").withLeftTable("B").withRightTable("C").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("CD").withLeftTable("C").withRightTable("D").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("DE").withLeftTable("D").withRightTable("E").withType(JoinType.ONE_TO_ONE));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("EF").withLeftTable("E").withRightTable("F").withType(JoinType.ONE_TO_MANY));
|
||||
qInstance.addJoin(new QJoinMetaData().withName("FG").withLeftTable("F").withRightTable("G").withType(JoinType.ONE_TO_ONE));
|
||||
|
||||
assertTrue(qInstance.getTable("A").getExposedJoins().get(0).getIsMany());
|
||||
assertFalse(qInstance.getTable("B").getExposedJoins().get(0).getIsMany());
|
||||
assertFalse(qInstance.getTable("F").getExposedJoins().get(0).getIsMany());
|
||||
}
|
||||
|
||||
}
|
@ -128,38 +128,31 @@ class EnumerationQueryActionTest extends BaseTest
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName("statesEnum");
|
||||
queryInput.setSkip(0);
|
||||
queryInput.setLimit(null);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(0).withLimit(null));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of("Missouri", "Illinois"), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(1);
|
||||
queryInput.setLimit(null);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(1).withLimit(null));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of("Illinois"), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(2);
|
||||
queryInput.setLimit(null);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(2).withLimit(null));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of(), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(null);
|
||||
queryInput.setLimit(1);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(null).withLimit(1));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of("Missouri"), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(null);
|
||||
queryInput.setLimit(2);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(null).withLimit(2));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of("Missouri", "Illinois"), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(null);
|
||||
queryInput.setLimit(3);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(null).withLimit(3));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of("Missouri", "Illinois"), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
|
||||
queryInput.setSkip(null);
|
||||
queryInput.setLimit(0);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(null).withLimit(0));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(List.of(), queryOutput.getRecords().stream().map(r -> r.getValueString("name")).toList());
|
||||
}
|
||||
|
@ -341,14 +341,13 @@ class MemoryBackendModuleTest extends BaseTest
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(table.getName());
|
||||
queryInput.setLimit(2);
|
||||
queryInput.setFilter(new QQueryFilter().withLimit(2));
|
||||
assertEquals(2, new QueryAction().execute(queryInput).getRecords().size());
|
||||
|
||||
queryInput.setLimit(1);
|
||||
queryInput.setFilter(new QQueryFilter().withLimit(1));
|
||||
assertEquals(1, new QueryAction().execute(queryInput).getRecords().size());
|
||||
|
||||
queryInput.setSkip(4);
|
||||
queryInput.setLimit(3);
|
||||
queryInput.setFilter(new QQueryFilter().withSkip(4).withLimit(3));
|
||||
assertEquals(0, new QueryAction().execute(queryInput).getRecords().size());
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||
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;
|
||||
@ -548,6 +549,7 @@ public class TestUtils
|
||||
.withFieldName("storeId"))
|
||||
.withAssociation(new Association().withName("orderLine").withAssociatedTableName(TABLE_NAME_LINE_ITEM).withJoinName("orderLineItem"))
|
||||
.withAssociation(new Association().withName("extrinsics").withAssociatedTableName(TABLE_NAME_ORDER_EXTRINSIC).withJoinName("orderOrderExtrinsic"))
|
||||
.withExposedJoin(new ExposedJoin().withJoinTable(TABLE_NAME_LINE_ITEM))
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
@ -582,7 +584,7 @@ public class TestUtils
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||
.withField(new QFieldMetaData("orderId", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("lineNumber", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("sku", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("sku", QFieldType.STRING).withLabel("SKU"))
|
||||
.withField(new QFieldMetaData("quantity", QFieldType.INTEGER));
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,11 @@
|
||||
package com.kingsrook.qqq.backend.core.utils.collections;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -48,4 +50,22 @@ class MutableListTest extends BaseTest
|
||||
list.remove(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNullInput()
|
||||
{
|
||||
List<Integer> list = new MutableList<>(null);
|
||||
list.add(1);
|
||||
assertEquals(1, list.size());
|
||||
|
||||
MutableList<Integer> mutableList = new MutableList<>(null, LinkedList::new);
|
||||
mutableList.add(1);
|
||||
assertEquals(1, mutableList.size());
|
||||
assertEquals(LinkedList.class, mutableList.getUnderlyingList().getClass());
|
||||
}
|
||||
|
||||
}
|
@ -22,9 +22,11 @@
|
||||
package com.kingsrook.qqq.backend.core.utils.collections;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -48,4 +50,22 @@ class MutableMapTest extends BaseTest
|
||||
map.putAll(Map.of("c", 3, "d", 4));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNullInput()
|
||||
{
|
||||
Map<Integer, String> map = new MutableMap<>(null);
|
||||
map.put(1, "one");
|
||||
assertEquals(1, map.size());
|
||||
|
||||
MutableMap<Integer, String> mutableMap = new MutableMap<>(null, LinkedHashMap::new);
|
||||
mutableMap.put(1, "uno");
|
||||
assertEquals(1, mutableMap.size());
|
||||
assertEquals(LinkedHashMap.class, mutableMap.getUnderlyingMap().getClass());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user