mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
CE-1115 pre-QA commit on saved report UI, including:
- adding pre-insert/update validation - move json field value formatting from post-query customizer to a FieldDisplayBehavior instead (works for audits this way :)
This commit is contained in:
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.savedreports;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableDefinition;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableGroupBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableValue;
|
||||
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.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeFunction;
|
||||
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.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for SavedReportJsonFieldDisplayValueFormatter
|
||||
*******************************************************************************/
|
||||
class SavedReportJsonFieldDisplayValueFormatterTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void beforeEach() throws QException
|
||||
{
|
||||
QContext.getQInstance().add(new SavedReportsMetaDataProvider().defineSavedReportTable(TestUtils.MEMORY_BACKEND_NAME, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPostQuery() throws QException
|
||||
{
|
||||
UnsafeFunction<SavedReport, QRecord, QException> customize = savedReport ->
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(SavedReport.TABLE_NAME);
|
||||
|
||||
QRecord record = savedReport.toQRecord();
|
||||
|
||||
for(String fieldName : List.of("queryFilterJson", "columnsJson", "pivotTableJson"))
|
||||
{
|
||||
SavedReportJsonFieldDisplayValueFormatter.getInstance().apply(ValueBehaviorApplier.Action.FORMATTING, List.of(record), qInstance, table, table.getField(fieldName));
|
||||
}
|
||||
|
||||
return (record);
|
||||
};
|
||||
|
||||
{
|
||||
QRecord record = customize.apply(new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()))
|
||||
.withPivotTableJson(JsonUtils.toJson(new PivotTableDefinition())));
|
||||
|
||||
assertEquals("0 Filters", record.getDisplayValue("queryFilterJson"));
|
||||
assertEquals("0 Columns", record.getDisplayValue("columnsJson"));
|
||||
assertEquals("0 Rows, 0 Columns, and 0 Values", record.getDisplayValue("pivotTableJson"));
|
||||
}
|
||||
|
||||
{
|
||||
QRecord record = customize.apply(new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns())));
|
||||
|
||||
assertEquals("0 Filters", record.getDisplayValue("queryFilterJson"));
|
||||
assertEquals("0 Columns", record.getDisplayValue("columnsJson"));
|
||||
assertNull(record.getDisplayValue("pivotTableJson"));
|
||||
}
|
||||
|
||||
{
|
||||
QRecord record = customize.apply(new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.IS_NOT_BLANK))))
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()
|
||||
.withColumn(new ReportColumn().withName("birthDate"))))
|
||||
.withPivotTableJson(JsonUtils.toJson(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy())
|
||||
.withValue(new PivotTableValue())
|
||||
)));
|
||||
|
||||
assertEquals("1 Filter", record.getDisplayValue("queryFilterJson"));
|
||||
assertEquals("1 Column", record.getDisplayValue("columnsJson"));
|
||||
assertEquals("1 Row, 0 Columns, and 1 Value", record.getDisplayValue("pivotTableJson"));
|
||||
}
|
||||
|
||||
{
|
||||
QRecord record = customize.apply(new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("id", QCriteriaOperator.GREATER_THAN, 1))
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.IS_NOT_BLANK))))
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()
|
||||
.withColumn(new ReportColumn().withName("__check__").withIsVisible(true))
|
||||
.withColumn(new ReportColumn().withName("id"))
|
||||
.withColumn(new ReportColumn().withName("firstName").withIsVisible(true))
|
||||
.withColumn(new ReportColumn().withName("lastName").withIsVisible(false))
|
||||
.withColumn(new ReportColumn().withName("birthDate"))))
|
||||
.withPivotTableJson(JsonUtils.toJson(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy())
|
||||
.withRow(new PivotTableGroupBy())
|
||||
.withColumn(new PivotTableGroupBy())
|
||||
.withValue(new PivotTableValue())
|
||||
.withValue(new PivotTableValue())
|
||||
.withValue(new PivotTableValue())
|
||||
)));
|
||||
|
||||
assertEquals("2 Filters", record.getDisplayValue("queryFilterJson"));
|
||||
assertEquals("3 Columns", record.getDisplayValue("columnsJson"));
|
||||
assertEquals("2 Rows, 1 Column, and 3 Values", record.getDisplayValue("pivotTableJson"));
|
||||
}
|
||||
|
||||
{
|
||||
QRecord record = customize.apply(new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson("blah")
|
||||
.withColumnsJson("<xml?>")
|
||||
.withPivotTableJson("{]"));
|
||||
|
||||
assertEquals("Invalid Filter...", record.getDisplayValue("queryFilterJson"));
|
||||
assertEquals("Invalid Columns...", record.getDisplayValue("columnsJson"));
|
||||
assertEquals("Invalid Pivot Table...", record.getDisplayValue("pivotTableJson"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2024. 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.savedreports;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableDefinition;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableFunction;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableGroupBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableValue;
|
||||
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.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for SavedReportTableCustomizer
|
||||
*******************************************************************************/
|
||||
class SavedReportTableCustomizerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void beforeEach() throws QException
|
||||
{
|
||||
QContext.getQInstance().add(new SavedReportsMetaDataProvider().defineSavedReportTable(TestUtils.MEMORY_BACKEND_NAME, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPreInsertAndPreUpdateAreWired() throws QException
|
||||
{
|
||||
SavedReport badRecord = new SavedReport()
|
||||
.withLabel("My Report")
|
||||
.withTableName("notATable");
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// assertions to apply both to a failed insert and a failed update //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
Consumer<QRecord> asserter = record -> assertThat(record.getErrors())
|
||||
.hasSizeGreaterThanOrEqualTo(2)
|
||||
.anyMatch(e -> e.getMessage().contains("Unrecognized table name"))
|
||||
.anyMatch(e -> e.getMessage().contains("must contain at least 1 column"));
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// go through insert action, to ensure wired-up correctly //
|
||||
////////////////////////////////////////////////////////////
|
||||
InsertOutput insertOutput = new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(badRecord));
|
||||
asserter.accept(insertOutput.getRecords().get(0));
|
||||
|
||||
////////////////////////////////
|
||||
// likewise for update action //
|
||||
////////////////////////////////
|
||||
UpdateOutput updateOutput = new UpdateAction().execute(new UpdateInput(SavedReport.TABLE_NAME).withRecordEntity(badRecord));
|
||||
asserter.accept(updateOutput.getRecords().get(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testParseFails()
|
||||
{
|
||||
QRecord record = new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson("...")
|
||||
.withColumnsJson("x")
|
||||
.withPivotTableJson("[")
|
||||
.toQRecord();
|
||||
|
||||
new SavedReportTableCustomizer().preValidateRecord(record);
|
||||
|
||||
assertThat(record.getErrors())
|
||||
.hasSize(3)
|
||||
.anyMatch(e -> e.getMessage().contains("Unable to parse queryFilterJson"))
|
||||
.anyMatch(e -> e.getMessage().contains("Unable to parse columnsJson"))
|
||||
.anyMatch(e -> e.getMessage().contains("Unable to parse pivotTableJson"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNoColumns()
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// given a reportColumns object, serialize it to json, put it in a saved report record, and run the pre-validator //
|
||||
// then assert we got error saying there were no columns. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
Consumer<ReportColumns> asserter = reportColumns ->
|
||||
{
|
||||
SavedReport savedReport = new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withColumnsJson(JsonUtils.toJson(reportColumns));
|
||||
|
||||
QRecord record = savedReport.toQRecord();
|
||||
new SavedReportTableCustomizer().preValidateRecord(record);
|
||||
|
||||
assertThat(record.getErrors())
|
||||
.hasSize(1)
|
||||
.anyMatch(e -> e.getMessage().contains("must contain at least 1 column"));
|
||||
};
|
||||
|
||||
asserter.accept(new ReportColumns());
|
||||
asserter.accept(new ReportColumns().withColumns(null));
|
||||
asserter.accept(new ReportColumns().withColumns(new ArrayList<>()));
|
||||
asserter.accept(new ReportColumns().withColumn(new ReportColumn()
|
||||
.withName("id").withIsVisible(false)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotTables()
|
||||
{
|
||||
BiConsumer<PivotTableDefinition, List<String>> asserter = (PivotTableDefinition ptd, List<String> expectedAnyMessageToContain) ->
|
||||
{
|
||||
SavedReport savedReport = new SavedReport()
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()
|
||||
.withColumn("id")
|
||||
.withColumn("firstName")
|
||||
.withColumn("lastName")
|
||||
.withColumn("birthDate")))
|
||||
.withPivotTableJson(JsonUtils.toJson(ptd));
|
||||
|
||||
QRecord record = savedReport.toQRecord();
|
||||
new SavedReportTableCustomizer().preValidateRecord(record);
|
||||
|
||||
assertThat(record.getErrors()).hasSize(expectedAnyMessageToContain.size());
|
||||
|
||||
for(String expected : expectedAnyMessageToContain)
|
||||
{
|
||||
assertThat(record.getErrors())
|
||||
.anyMatch(e -> e.getMessage().contains(expected));
|
||||
}
|
||||
};
|
||||
|
||||
asserter.accept(new PivotTableDefinition(), List.of("must contain at least 1 row"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withRow(new PivotTableGroupBy()),
|
||||
List.of("Missing field name for at least one pivot table row"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withRow(new PivotTableGroupBy().withFieldName("createDate")),
|
||||
List.of("row is using field (Create Date) which is not an active column"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withColumn(new PivotTableGroupBy()),
|
||||
List.of("Missing field name for at least one pivot table column"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withColumn(new PivotTableGroupBy().withFieldName("createDate")),
|
||||
List.of("column is using field (Create Date) which is not an active column"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withValue(new PivotTableValue().withFunction(PivotTableFunction.SUM)),
|
||||
List.of("Missing field name for at least one pivot table value"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withValue(new PivotTableValue().withFieldName("createDate").withFunction(PivotTableFunction.SUM)),
|
||||
List.of("value is using field (Create Date) which is not an active column"));
|
||||
|
||||
asserter.accept(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("id"))
|
||||
.withValue(new PivotTableValue().withFieldName("firstName")),
|
||||
List.of("Missing function for at least one pivot table value"));
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user