mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
Merge pull request #79 from Kingsrook/feature/CE-881-create-basic-saved-reports
Feature/ce 881 create basic saved reports
This commit is contained in:
@ -36,6 +36,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
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.ReportDestination;
|
||||
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;
|
||||
@ -43,6 +44,7 @@ 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.LocalMacDevUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@ -118,6 +120,26 @@ class ExportActionTest extends BaseTest
|
||||
runReport(recordCount, filename, ReportFormat.XLSX, true);
|
||||
|
||||
File file = new File(filename);
|
||||
LocalMacDevUtils.openFile(file.getAbsolutePath());
|
||||
|
||||
assertTrue(file.delete());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testExcelPOI() throws Exception
|
||||
{
|
||||
int recordCount = 1000;
|
||||
String filename = "/tmp/ReportActionTest-POI.xlsx";
|
||||
|
||||
runReport(recordCount, filename, ReportFormat.XLSX, true);
|
||||
|
||||
File file = new File(filename);
|
||||
LocalMacDevUtils.openFile(file.getAbsolutePath());
|
||||
|
||||
assertTrue(file.delete());
|
||||
}
|
||||
@ -147,9 +169,10 @@ class ExportActionTest extends BaseTest
|
||||
ExportInput exportInput = new ExportInput();
|
||||
exportInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||
|
||||
exportInput.setReportFormat(ReportFormat.CSV);
|
||||
ByteArrayOutputStream reportOutputStream = new ByteArrayOutputStream();
|
||||
exportInput.setReportOutputStream(reportOutputStream);
|
||||
exportInput.setReportDestination(new ReportDestination()
|
||||
.withReportFormat(ReportFormat.CSV)
|
||||
.withReportOutputStream(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"));
|
||||
@ -197,8 +220,7 @@ class ExportActionTest extends BaseTest
|
||||
exportInput.setTableName("person");
|
||||
QTableMetaData table = exportInput.getTable();
|
||||
|
||||
exportInput.setReportFormat(reportFormat);
|
||||
exportInput.setReportOutputStream(outputStream);
|
||||
exportInput.setReportDestination(new ReportDestination().withReportFormat(reportFormat).withReportOutputStream(outputStream));
|
||||
exportInput.setQueryFilter(new QQueryFilter());
|
||||
exportInput.setLimit(recordCount);
|
||||
|
||||
@ -243,7 +265,7 @@ class ExportActionTest extends BaseTest
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// use xlsx, which has a max-rows limit, to verify that code runs, but doesn't throw when there aren't too many rows //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
exportInput.setReportFormat(ReportFormat.XLSX);
|
||||
exportInput.setReportDestination(new ReportDestination().withReportFormat(ReportFormat.XLSX));
|
||||
|
||||
new ExportAction().preExecute(exportInput);
|
||||
|
||||
@ -278,7 +300,7 @@ class ExportActionTest extends BaseTest
|
||||
////////////////////////////////////////////////////////////////
|
||||
// use xlsx, which has a max-cols limit, to verify that code. //
|
||||
////////////////////////////////////////////////////////////////
|
||||
exportInput.setReportFormat(ReportFormat.XLSX);
|
||||
exportInput.setReportDestination(new ReportDestination().withReportFormat(ReportFormat.XLSX));
|
||||
|
||||
assertThrows(QUserFacingException.class, () ->
|
||||
{
|
||||
|
@ -23,19 +23,33 @@ package com.kingsrook.qqq.backend.core.actions.reporting;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.fastexcel.ExcelFastexcelExportStreamer;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.BoldHeaderAndFooterPoiExcelStyler;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.ExcelPoiBasedStreamingExportStreamer;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.PoiExcelStylerInterface;
|
||||
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.ReportDestination;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
||||
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.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
@ -51,7 +65,17 @@ 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.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.testutils.PersonQRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.LocalMacDevUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFPivotTable;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -85,14 +109,14 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivot1() throws QException
|
||||
void testSummary1() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(definePersonShoesPivotReport(true));
|
||||
qInstance.addReport(definePersonShoesSummaryReport(true));
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.of(1980, Month.JANUARY, 1), "endDate", LocalDate.of(1980, Month.DECEMBER, 31)));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("pivot");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("summary");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(3, list.size());
|
||||
@ -140,10 +164,10 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivot2() throws QException
|
||||
void testSummary2() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = definePersonShoesPivotReport(false);
|
||||
QReportMetaData report = definePersonShoesSummaryReport(false);
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// change from the default to sort reversed //
|
||||
@ -153,7 +177,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.of(1980, Month.JANUARY, 1), "endDate", LocalDate.of(1980, Month.DECEMBER, 31)));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("pivot");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("summary");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(2, list.size());
|
||||
@ -172,10 +196,10 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivot3() throws QException
|
||||
void testSummary3() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = definePersonShoesPivotReport(false);
|
||||
QReportMetaData report = definePersonShoesSummaryReport(false);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// remove the filters, change to sort by personCount (to get some ties), then sumPrice desc //
|
||||
@ -187,7 +211,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.now(), "endDate", LocalDate.now()));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("pivot");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("summary");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
|
||||
@ -224,16 +248,16 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivot4() throws QException
|
||||
void testSummary4() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = definePersonShoesPivotReport(false);
|
||||
QReportMetaData report = definePersonShoesSummaryReport(false);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// remove the filter, change to have 2 pivot columns - homeStateId and lastName - we should get no roll-up like this. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
report.getDataSources().get(0).getQueryFilter().setCriteria(null);
|
||||
report.getViews().get(0).setPivotFields(List.of(
|
||||
report.getViews().get(0).setSummaryFields(List.of(
|
||||
"homeStateId",
|
||||
"lastName"
|
||||
));
|
||||
@ -241,7 +265,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.now(), "endDate", LocalDate.now()));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("pivot");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("summary");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(6, list.size());
|
||||
@ -282,21 +306,21 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivot5() throws QException
|
||||
void testSummary5() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = definePersonShoesPivotReport(false);
|
||||
QReportMetaData report = definePersonShoesSummaryReport(false);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// remove the filter, and just pivot on homeStateId - should aggregate differently //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
report.getDataSources().get(0).getQueryFilter().setCriteria(null);
|
||||
report.getViews().get(0).setPivotFields(List.of("homeStateId"));
|
||||
report.getViews().get(0).setSummaryFields(List.of("homeStateId"));
|
||||
qInstance.addReport(report);
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.now(), "endDate", LocalDate.now()));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("pivot");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("summary");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(2, list.size());
|
||||
@ -315,23 +339,18 @@ public class GenerateReportActionTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runToCsv() throws Exception
|
||||
private String runToString(ReportFormat reportFormat, String reportName) throws Exception
|
||||
{
|
||||
String name = "/tmp/report.csv";
|
||||
String name = "/tmp/report." + reportFormat.getExtension();
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(definePersonShoesPivotReport(true));
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportFormat(ReportFormat.CSV);
|
||||
reportInput.setReportOutputStream(fileOutputStream);
|
||||
reportInput.setReportName(reportName);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(reportFormat).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
return (FileUtils.readFileToString(new File(name), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,27 +360,189 @@ public class GenerateReportActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runToXlsx() throws Exception
|
||||
void runSummaryToXlsx() throws Exception
|
||||
{
|
||||
String name = "/tmp/report.xlsx";
|
||||
ReportFormat format = ReportFormat.XLSX;
|
||||
String name = "/tmp/report-" + format + ".xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(definePersonShoesPivotReport(true));
|
||||
qInstance.addReport(definePersonShoesSummaryReport(true));
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportFormat(ReportFormat.XLSX);
|
||||
reportInput.setReportOutputStream(fileOutputStream);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(format).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Keep some test coverage on the fastexcel library (as long as we keep it around)
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runTableToXlsxFastexcel() throws Exception
|
||||
{
|
||||
ReportFormat format = ReportFormat.XLSX;
|
||||
String name = "/tmp/report-fastexcel.xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(defineTableOnlyReport());
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(format).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
reportInput.setOverrideExportStreamerSupplier(ExcelFastexcelExportStreamer::new);
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Imagine if we used the boldHeaderAndFooter styler
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runTableToXlsxWithOverrideStyles() throws Exception
|
||||
{
|
||||
ReportFormat format = ReportFormat.XLSX;
|
||||
String name = "/tmp/report-fastexcel.xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData reportMetaData = defineTableOnlyReport();
|
||||
reportMetaData.getViews().get(0).withTitleFormat("My Title");
|
||||
|
||||
qInstance.addReport(reportMetaData);
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(format).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
|
||||
reportInput.setOverrideExportStreamerSupplier(() -> new ExcelPoiBasedStreamingExportStreamer()
|
||||
{
|
||||
@Override
|
||||
protected PoiExcelStylerInterface getStylerInterface()
|
||||
{
|
||||
return new BoldHeaderAndFooterPoiExcelStyler();
|
||||
}
|
||||
});
|
||||
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runTableToXlsx() throws Exception
|
||||
{
|
||||
ReportFormat format = ReportFormat.XLSX;
|
||||
String name = "/tmp/report-" + format + ".xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(defineTableOnlyReport());
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(format).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runPivotToXlsx() throws Exception
|
||||
{
|
||||
String name = "/tmp/pivot-test.xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(definePivotReport());
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(ReportFormat.XLSX).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name, "/Applications/Numbers.app");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// read the file we wrote, and assert about its contents //
|
||||
///////////////////////////////////////////////////////////
|
||||
FileInputStream file = new FileInputStream(name);
|
||||
XSSFWorkbook workbook = new XSSFWorkbook(file);
|
||||
|
||||
XSSFSheet sheet = workbook.getSheetAt(1);
|
||||
List<XSSFPivotTable> pivotTables = sheet.getPivotTables();
|
||||
XSSFPivotTable xssfPivotTable = pivotTables.get(0);
|
||||
List<Integer> rowLabelColumns = xssfPivotTable.getRowLabelColumns();
|
||||
List<Integer> colLabelColumns = xssfPivotTable.getColLabelColumns();
|
||||
Sheet dataSheet = xssfPivotTable.getDataSheet();
|
||||
Sheet parentSheet = xssfPivotTable.getParentSheet();
|
||||
System.out.println();
|
||||
|
||||
Map<Integer, List<Object>> data = new HashMap<>();
|
||||
int i = 0;
|
||||
for(Row row : sheet)
|
||||
{
|
||||
data.put(i, new ArrayList<>());
|
||||
|
||||
for(Cell cell : row)
|
||||
{
|
||||
data.get(i).add(switch(cell.getCellType())
|
||||
{
|
||||
case _NONE -> "<_NONE>";
|
||||
case NUMERIC -> cell.getNumericCellValue();
|
||||
case STRING -> cell.getStringCellValue();
|
||||
case FORMULA -> cell.getCellFormula();
|
||||
case BLANK -> "<BLANK>";
|
||||
case BOOLEAN -> cell.getBooleanCellValue();
|
||||
case ERROR -> cell.getErrorCellValue();
|
||||
});
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
System.out.println(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -369,8 +550,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
{
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportFormat(ReportFormat.LIST_OF_MAPS);
|
||||
reportInput.setReportOutputStream(new ByteArrayOutputStream());
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(ReportFormat.LIST_OF_MAPS).withReportOutputStream(new ByteArrayOutputStream()));
|
||||
reportInput.setInputValues(inputValues);
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
}
|
||||
@ -380,15 +560,15 @@ public class GenerateReportActionTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void insertPersonRecords(QInstance qInstance) throws QException
|
||||
public static void insertPersonRecords(QInstance qInstance) throws QException
|
||||
{
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
|
||||
new PersonQRecord().withLastName("Jonson").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(null).withHomeStateId(1).withPrice(null).withCost(new BigDecimal("0.50")), // wrong last initial
|
||||
new PersonQRecord().withLastName("Jones").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(3).withHomeStateId(1).withPrice(new BigDecimal("1.00")).withCost(new BigDecimal("0.50")), // wrong last initial
|
||||
new PersonQRecord().withLastName("Kelly").withBirthDate(LocalDate.of(1979, Month.DECEMBER, 30)).withNoOfShoes(4).withHomeStateId(1).withPrice(new BigDecimal("1.20")).withCost(new BigDecimal("0.50")), // bad birthdate
|
||||
new PersonQRecord().withLastName("Keller").withBirthDate(LocalDate.of(1980, Month.JANUARY, 7)).withNoOfShoes(5).withHomeStateId(1).withPrice(new BigDecimal("2.40")).withCost(new BigDecimal("3.50")),
|
||||
new PersonQRecord().withLastName("Kelkhoff").withBirthDate(LocalDate.of(1980, Month.FEBRUARY, 15)).withNoOfShoes(6).withHomeStateId(1).withPrice(new BigDecimal("3.60")).withCost(new BigDecimal("3.50")),
|
||||
new PersonQRecord().withLastName("Kelkhoff").withBirthDate(LocalDate.of(1980, Month.MARCH, 20)).withNoOfShoes(7).withHomeStateId(2).withPrice(new BigDecimal("4.80")).withCost(new BigDecimal("3.50"))
|
||||
new PersonQRecord().withFirstName("Darin").withLastName("Jonson").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(null).withHomeStateId(1).withPrice(null).withCost(new BigDecimal("0.50")), // wrong last initial
|
||||
new PersonQRecord().withFirstName("Darin").withLastName("Jones").withBirthDate(LocalDate.of(1980, Month.JANUARY, 31)).withNoOfShoes(3).withHomeStateId(1).withPrice(new BigDecimal("1.00")).withCost(new BigDecimal("0.50")), // wrong last initial
|
||||
new PersonQRecord().withFirstName("Darin").withLastName("Kelly").withBirthDate(LocalDate.of(1979, Month.DECEMBER, 30)).withNoOfShoes(4).withHomeStateId(1).withPrice(new BigDecimal("1.20")).withCost(new BigDecimal("0.50")), // bad birthdate
|
||||
new PersonQRecord().withFirstName("Trevor").withLastName("Keller").withBirthDate(LocalDate.of(1980, Month.JANUARY, 7)).withNoOfShoes(5).withHomeStateId(1).withPrice(new BigDecimal("2.40")).withCost(new BigDecimal("3.50")),
|
||||
new PersonQRecord().withFirstName("Trevor").withLastName("Kelkhoff").withBirthDate(LocalDate.of(1980, Month.FEBRUARY, 15)).withNoOfShoes(6).withHomeStateId(1).withPrice(new BigDecimal("3.60")).withCost(new BigDecimal("3.50")),
|
||||
new PersonQRecord().withFirstName("Kelly").withLastName("Kelkhoff").withBirthDate(LocalDate.of(1980, Month.MARCH, 20)).withNoOfShoes(7).withHomeStateId(2).withPrice(new BigDecimal("4.80")).withCost(new BigDecimal("3.50"))
|
||||
));
|
||||
}
|
||||
|
||||
@ -397,7 +577,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QReportMetaData definePersonShoesPivotReport(boolean includeTotalRow)
|
||||
public static QReportMetaData definePersonShoesSummaryReport(boolean includeTotalRow)
|
||||
{
|
||||
return new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
@ -416,11 +596,11 @@ public class GenerateReportActionTest extends BaseTest
|
||||
))
|
||||
.withViews(List.of(
|
||||
new QReportView()
|
||||
.withName("pivot")
|
||||
.withLabel("pivot")
|
||||
.withName("summary")
|
||||
.withLabel("summary")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.SUMMARY)
|
||||
.withPivotFields(List.of("lastName"))
|
||||
.withSummaryFields(List.of("lastName"))
|
||||
.withIncludeTotalRow(includeTotalRow)
|
||||
.withTitleFormat("Number of shoes - people born between %s and %s - pivot on LastName, sort by Quantity, Revenue DESC")
|
||||
.withTitleFields(List.of("${input.startDate}", "${input.endDate}"))
|
||||
@ -452,39 +632,14 @@ public class GenerateReportActionTest extends BaseTest
|
||||
@Test
|
||||
void testTableOnlyReport() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
.withDataSources(List.of(
|
||||
new QReportDataSource()
|
||||
.withName("persons")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("birthDate", QCriteriaOperator.GREATER_THAN, List.of("${input.startDate}")))
|
||||
)
|
||||
))
|
||||
.withInputFields(List.of(
|
||||
new QFieldMetaData("startDate", QFieldType.DATE_TIME)
|
||||
))
|
||||
.withViews(List.of(
|
||||
new QReportView()
|
||||
.withName("table1")
|
||||
.withLabel("table1")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
new QReportField().withName("id"),
|
||||
new QReportField().withName("firstName"),
|
||||
new QReportField().withName("lastName")
|
||||
))
|
||||
));
|
||||
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = defineTableOnlyReport();
|
||||
qInstance.addReport(report);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.of(1980, Month.JANUARY, 1)));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("table1");
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("Table 1");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(5, list.size());
|
||||
@ -493,6 +648,88 @@ public class GenerateReportActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QReportMetaData defineTableOnlyReport()
|
||||
{
|
||||
QReportMetaData report = new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
.withDataSources(List.of(
|
||||
new QReportDataSource()
|
||||
.withName("persons")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("birthDate", QCriteriaOperator.GREATER_THAN, List.of("${input.startDate}"))))))
|
||||
|
||||
.withInputFields(List.of(
|
||||
new QFieldMetaData("startDate", QFieldType.DATE_TIME)))
|
||||
|
||||
.withViews(List.of(
|
||||
new QReportView()
|
||||
.withName("table1")
|
||||
.withLabel("Table 1")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
new QReportField().withName("id"),
|
||||
new QReportField().withName("firstName"),
|
||||
new QReportField().withName("lastName")))));
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QReportMetaData definePivotReport()
|
||||
{
|
||||
QReportMetaData report = new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
.withDataSources(List.of(
|
||||
new QReportDataSource()
|
||||
.withName("persons")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("birthDate", QCriteriaOperator.GREATER_THAN, List.of("${input.startDate}"))))))
|
||||
|
||||
.withInputFields(List.of(
|
||||
new QFieldMetaData("startDate", QFieldType.DATE_TIME)))
|
||||
|
||||
.withViews(List.of(
|
||||
|
||||
new QReportView()
|
||||
.withName("table1")
|
||||
.withLabel("Table 1")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
new QReportField().withName("id"),
|
||||
new QReportField().withName("firstName"),
|
||||
new QReportField().withName("lastName"),
|
||||
new QReportField().withName("homeStateId")
|
||||
)),
|
||||
|
||||
new QReportView()
|
||||
.withName("pivotTable1")
|
||||
.withLabel("My Pivot Table")
|
||||
|
||||
.withType(ReportType.PIVOT)
|
||||
.withPivotTableSourceViewName("table1")
|
||||
.withPivotTableDefinition(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("homeStateId"))
|
||||
// .withRow(new PivotTableGroupBy().withFieldName("lastName"))
|
||||
// .withColumn(new PivotTableGroupBy().withFieldName("firstName"))
|
||||
.withValue(new PivotTableValue().withFunction(PivotTableFunction.COUNT).withFieldName("id")))
|
||||
));
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -500,6 +737,180 @@ public class GenerateReportActionTest extends BaseTest
|
||||
void testTwoTableViewsOneDataSourceReport() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
defineTwoViewsOneDataSourceReport(qInstance);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.of(1980, Month.JANUARY, 1)));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("Table 1");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(5, list.size());
|
||||
assertThat(row).containsOnlyKeys("Id", "First Name", "Last Name");
|
||||
|
||||
list = ListOfMapsExportStreamer.getList("Table 2");
|
||||
iterator = list.iterator();
|
||||
row = iterator.next();
|
||||
assertEquals(5, list.size());
|
||||
assertThat(row).containsOnlyKeys("Birth Date");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testOneTableViewsOneDataSourceJsonReport() throws Exception
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = defineTableOnlyReport();
|
||||
qInstance.addReport(report);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
String json = runToString(ReportFormat.JSON, report.getName());
|
||||
// System.out.println(json);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// for a one-view report, we should just have an array of the report's records //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
JSONArray jsonArray = new JSONArray(json);
|
||||
assertEquals(6, jsonArray.length());
|
||||
assertThat(jsonArray.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("id", 1)
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin")
|
||||
.hasFieldOrPropertyWithValue("lastName", "Jonson");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTwoTableViewsOneDataSourceJsonReport() throws Exception
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = defineTwoViewsOneDataSourceReport(qInstance);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
String json = runToString(ReportFormat.JSON, report.getName());
|
||||
// System.out.println(json);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// for a multi-view report, we should have an array with the views as elements //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
JSONArray jsonArray = new JSONArray(json);
|
||||
assertEquals(2, jsonArray.length());
|
||||
|
||||
JSONObject firstView = jsonArray.getJSONObject(0);
|
||||
assertEquals("Table 1", firstView.getString("name"));
|
||||
JSONArray firstViewData = firstView.getJSONArray("data");
|
||||
assertEquals(6, firstViewData.length());
|
||||
assertThat(firstViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("id", 1)
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin")
|
||||
.hasFieldOrPropertyWithValue("lastName", "Jonson");
|
||||
|
||||
JSONObject secondView = jsonArray.getJSONObject(1);
|
||||
assertEquals("Table 2", secondView.getString("name"));
|
||||
JSONArray secondViewData = secondView.getJSONArray("data");
|
||||
assertEquals(6, secondViewData.length());
|
||||
assertThat(secondViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("birthDate", "1980-01-31");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableViewsAndSummaryViewJsonReport() throws Exception
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QReportMetaData report = defineSimplePersonTableAndSummaryByFirstNameReport();
|
||||
qInstance.addReport(report);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
String json = runToString(ReportFormat.JSON, report.getName());
|
||||
System.out.println(json);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// for a multi-view report, we should have an array with the views as elements //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
JSONArray jsonArray = new JSONArray(json);
|
||||
assertEquals(2, jsonArray.length());
|
||||
|
||||
JSONObject firstView = jsonArray.getJSONObject(0);
|
||||
assertEquals("Table 1", firstView.getString("name"));
|
||||
JSONArray firstViewData = firstView.getJSONArray("data");
|
||||
assertEquals(6, firstViewData.length());
|
||||
assertThat(firstViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("id", 1)
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin")
|
||||
.hasFieldOrPropertyWithValue("lastName", "Jonson");
|
||||
|
||||
JSONObject secondView = jsonArray.getJSONObject(1);
|
||||
assertEquals("Summary", secondView.getString("name"));
|
||||
JSONArray secondViewData = secondView.getJSONArray("data");
|
||||
assertEquals(4, secondViewData.length());
|
||||
assertThat(secondViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin")
|
||||
.hasFieldOrPropertyWithValue("personCount", 3);
|
||||
assertThat(secondViewData.getJSONObject(3).toMap())
|
||||
.hasFieldOrPropertyWithValue("firstName", "Totals")
|
||||
.hasFieldOrPropertyWithValue("personCount", 6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QReportMetaData defineSimplePersonTableAndSummaryByFirstNameReport()
|
||||
{
|
||||
return new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
.withDataSources(List.of(
|
||||
new QReportDataSource()
|
||||
.withName("persons")
|
||||
.withSourceTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withQueryFilter(new QQueryFilter())
|
||||
))
|
||||
.withViews(List.of(
|
||||
new QReportView()
|
||||
.withName("table1")
|
||||
.withLabel("Table 1")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
new QReportField().withName("id"),
|
||||
new QReportField().withName("firstName"),
|
||||
new QReportField().withName("lastName"),
|
||||
new QReportField().withName("homeStateId")
|
||||
)),
|
||||
new QReportView()
|
||||
.withName("summary")
|
||||
.withLabel("Summary")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.SUMMARY)
|
||||
.withSummaryFields(List.of("firstName"))
|
||||
.withIncludeTotalRow(true)
|
||||
.withOrderByFields(List.of(new QFilterOrderBy("personCount", false)))
|
||||
.withColumns(List.of(
|
||||
new QReportField().withName("personCount").withLabel("Person Count").withFormula("${pivot.count.id}").withDisplayFormat(DisplayFormat.COMMAS)
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QReportMetaData defineTwoViewsOneDataSourceReport(QInstance qInstance)
|
||||
{
|
||||
QReportMetaData report = new QReportMetaData()
|
||||
.withName(REPORT_NAME)
|
||||
.withDataSources(List.of(
|
||||
@ -516,7 +927,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
.withViews(List.of(
|
||||
new QReportView()
|
||||
.withName("table1")
|
||||
.withLabel("table1")
|
||||
.withLabel("Table 1")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
@ -526,7 +937,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
)),
|
||||
new QReportView()
|
||||
.withName("table2")
|
||||
.withLabel("table2")
|
||||
.withLabel("Table 2")
|
||||
.withDataSourceName("persons")
|
||||
.withType(ReportType.TABLE)
|
||||
.withColumns(List.of(
|
||||
@ -535,21 +946,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
));
|
||||
|
||||
qInstance.addReport(report);
|
||||
|
||||
insertPersonRecords(qInstance);
|
||||
runReport(qInstance, Map.of("startDate", LocalDate.of(1980, Month.JANUARY, 1)));
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("table1");
|
||||
Iterator<Map<String, String>> iterator = list.iterator();
|
||||
Map<String, String> row = iterator.next();
|
||||
assertEquals(5, list.size());
|
||||
assertThat(row).containsOnlyKeys("Id", "First Name", "Last Name");
|
||||
|
||||
list = ListOfMapsExportStreamer.getList("table2");
|
||||
iterator = list.iterator();
|
||||
row = iterator.next();
|
||||
assertEquals(5, list.size());
|
||||
assertThat(row).containsOnlyKeys("Birth Date");
|
||||
return (report);
|
||||
}
|
||||
|
||||
|
||||
@ -566,8 +963,7 @@ public class GenerateReportActionTest extends BaseTest
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(TestUtils.REPORT_NAME_PERSON_SIMPLE);
|
||||
reportInput.setReportFormat(ReportFormat.LIST_OF_MAPS);
|
||||
reportInput.setReportOutputStream(new ByteArrayOutputStream());
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(ReportFormat.LIST_OF_MAPS).withReportOutputStream(new ByteArrayOutputStream()));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
|
||||
List<Map<String, String>> list = ListOfMapsExportStreamer.getList("Simple Report");
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.actions.reporting;
|
||||
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for JsonExportStreamer
|
||||
*******************************************************************************/
|
||||
class JsonExportStreamerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test()
|
||||
{
|
||||
Function<String, String> runOne = label -> new JsonExportStreamer().getLabelForJson(new QFieldMetaData("test", QFieldType.STRING).withLabel(label));
|
||||
assertEquals("sku", runOne.apply("SKU"));
|
||||
assertEquals("clientName", runOne.apply("Client Name"));
|
||||
assertEquals("slaStatus", runOne.apply("SLA Status"));
|
||||
assertEquals("lineItem:sku", runOne.apply("Line Item: SKU"));
|
||||
assertEquals("parcel:slaStatus", runOne.apply("Parcel: SLA Status"));
|
||||
assertEquals("order:client", runOne.apply("Order: Client"));
|
||||
}
|
||||
|
||||
}
|
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateInpu
|
||||
import com.kingsrook.qqq.backend.core.model.actions.templates.RenderTemplateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.templates.TemplateType;
|
||||
import com.kingsrook.qqq.backend.core.utils.LocalMacDevUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
||||
@ -107,8 +108,8 @@ class ConvertHtmlToPdfActionTest extends BaseTest
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// for local dev on a mac, turn this on to auto-open the generated PDF //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// todo not commit
|
||||
// Runtime.getRuntime().exec(new String[] { "/usr/bin/open", "/tmp/file.pdf" });
|
||||
// LocalMacDevUtils.mayOpenFiles = true;
|
||||
LocalMacDevUtils.openFile("/tmp/file.pdf");
|
||||
}
|
||||
|
||||
}
|
@ -216,7 +216,7 @@ class QInstanceHelpContentManagerTest extends BaseTest
|
||||
// now - post-insert customizer should have automatically added help content to the instance //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
assertTrue(widget.getHelpContent().containsKey("label"));
|
||||
assertEquals("i need somebody", widget.getHelpContent().get("label").getContent());
|
||||
assertEquals("i need somebody", widget.getHelpContent().get("label").get(0).getContent());
|
||||
}
|
||||
|
||||
|
||||
|
@ -272,6 +272,25 @@ public class QInstanceValidatorTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test rules for process step names (must be set; must not be duplicated)
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateProcessStepNames()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE).getStepList().get(0).setName(null),
|
||||
"Missing name for a step at index");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE).getStepList().get(0).setName(""),
|
||||
"Missing name for a step at index");
|
||||
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE_INTERACTIVE).getStepList().forEach(s -> s.setName("myStep")),
|
||||
"Duplicate step name [myStep]", "Duplicate step name [myStep]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test that a process with a step that is a private class fails
|
||||
**
|
||||
|
@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
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.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@ -156,4 +157,47 @@ class DynamicDefaultValueBehaviorTest extends BaseTest
|
||||
assertNull(record.getValue("firstName"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUserId()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
table.getField("firstName").withBehavior(DynamicDefaultValueBehavior.USER_ID);
|
||||
|
||||
{
|
||||
////////////////////////////////
|
||||
// set it (if null) on insert //
|
||||
////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record), null);
|
||||
assertEquals(QContext.getQSession().getUser().getIdReference(), record.getValue("firstName"));
|
||||
}
|
||||
|
||||
{
|
||||
////////////////////////////////
|
||||
// set it (if null) on update //
|
||||
////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, qInstance, table, List.of(record), null);
|
||||
assertEquals(QContext.getQSession().getUser().getIdReference(), record.getValue("firstName"));
|
||||
}
|
||||
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// only set it if it wasn't previously set (both insert & update) //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 1).withValue("firstName", "Bob");
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record), null);
|
||||
assertEquals("Bob", record.getValue("firstName"));
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, qInstance, table, List.of(record), null);
|
||||
assertEquals("Bob", record.getValue("firstName"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -50,7 +50,7 @@ class BasicRunReportProcessTest extends BaseTest
|
||||
void testRunReport() throws QException
|
||||
{
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
QReportMetaData report = GenerateReportActionTest.definePersonShoesPivotReport(true);
|
||||
QReportMetaData report = GenerateReportActionTest.definePersonShoesSummaryReport(true);
|
||||
QProcessMetaData runReportProcess = BasicRunReportProcess.defineProcessMetaData();
|
||||
|
||||
instance.addReport(report);
|
||||
|
@ -0,0 +1,406 @@
|
||||
/*
|
||||
* 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.processes.implementations.savedreports;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.GenerateReportActionTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.actions.reporting.ReportFormatPossibleValueEnum;
|
||||
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.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.storage.StorageInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.savedreports.ReportColumns;
|
||||
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReport;
|
||||
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReportsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryStorageAction;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.LocalMacDevUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RenderSavedReportExecuteStep
|
||||
*******************************************************************************/
|
||||
class RenderSavedReportProcessTest extends BaseTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception
|
||||
{
|
||||
new SavedReportsMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
GenerateReportActionTest.insertPersonRecords(QContext.getQInstance());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@Disabled
|
||||
void testForDevPrintAPivotDefinitionAsJson()
|
||||
{
|
||||
System.out.println(JsonUtils.toPrettyJson(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy()
|
||||
.withFieldName("homeStateId"))
|
||||
.withRow(new PivotTableGroupBy()
|
||||
.withFieldName("firstName"))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("id")
|
||||
.withFunction(PivotTableFunction.COUNT))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("cost")
|
||||
.withFunction(PivotTableFunction.SUM))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableOnlyReport() throws Exception
|
||||
{
|
||||
String label = "Test Report";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do columns json as a string, rather than a toJson'ed ReportColumns object, //
|
||||
// to help verify that we don't choke on un-recognized properties (e.g., as QFMD sends) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
String columnsJson = """
|
||||
{"columns":[
|
||||
{"name": "k"},
|
||||
{"name": "id"},
|
||||
{"name": "firstName", "isVisible": true},
|
||||
{"name": "lastName", "pinned": "left"},
|
||||
{"name": "createDate", "isVisible": false}
|
||||
]}""";
|
||||
|
||||
QRecord savedReport = new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(new SavedReport()
|
||||
.withLabel(label)
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withColumnsJson(columnsJson)
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
)).getRecords().get(0);
|
||||
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.CSV);
|
||||
|
||||
String downloadFileName = runProcessOutput.getValueString("downloadFileName");
|
||||
assertThat(downloadFileName)
|
||||
.startsWith(label + " - ")
|
||||
.matches(".*\\d\\d\\d\\d-\\d\\d-\\d\\d-\\d\\d\\d\\d.*")
|
||||
.endsWith(".csv");
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
List<String> lines = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
|
||||
|
||||
assertEquals("""
|
||||
"Id","First Name","Last Name"
|
||||
""".trim(), lines.get(0));
|
||||
assertEquals("""
|
||||
"1","Darin","Jonson"
|
||||
""".trim(), lines.get(1));
|
||||
|
||||
writeTmpFileAndOpen(inputStream, ".csv");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static InputStream getInputStream(RunProcessOutput runProcessOutput) throws QException
|
||||
{
|
||||
String storageTableName = runProcessOutput.getValueString("storageTableName");
|
||||
String storageReference = runProcessOutput.getValueString("storageReference");
|
||||
InputStream inputStream = new MemoryStorageAction().getInputStream(new StorageInput(storageTableName).withReference(storageReference));
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void writeTmpFileAndOpen(InputStream inputStream, String suffix) throws IOException
|
||||
{
|
||||
// LocalMacDevUtils.mayOpenFiles = true;
|
||||
if(LocalMacDevUtils.mayOpenFiles)
|
||||
{
|
||||
inputStream.reset();
|
||||
|
||||
File tmpFile = File.createTempFile(getClass().getName(), suffix, new File("/tmp/"));
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
|
||||
inputStream.transferTo(fileOutputStream);
|
||||
fileOutputStream.close();
|
||||
|
||||
LocalMacDevUtils.openFile(tmpFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QRecord insertBasicSavedPivotReport(String label) throws QException
|
||||
{
|
||||
return new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(new SavedReport()
|
||||
.withLabel(label)
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()
|
||||
.withColumn("id")
|
||||
.withColumn("firstName")
|
||||
.withColumn("lastName")
|
||||
.withColumn("cost")
|
||||
.withColumn("birthDate")
|
||||
.withColumn("homeStateId")))
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withPivotTableJson(JsonUtils.toJson(new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy()
|
||||
.withFieldName("homeStateId"))
|
||||
.withRow(new PivotTableGroupBy()
|
||||
.withFieldName("firstName"))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("id")
|
||||
.withFunction(PivotTableFunction.COUNT))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("cost")
|
||||
.withFunction(PivotTableFunction.SUM))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("birthDate")
|
||||
.withFunction(PivotTableFunction.MIN))
|
||||
.withValue(new PivotTableValue()
|
||||
.withFieldName("birthDate")
|
||||
.withFunction(PivotTableFunction.MAX))
|
||||
))
|
||||
)).getRecords().get(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotXlsx() throws Exception
|
||||
{
|
||||
String label = "Test Pivot Report";
|
||||
QRecord savedReport = insertBasicSavedPivotReport(label);
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.XLSX);
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
writeTmpFileAndOpen(inputStream, ".xlsx");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotJson() throws Exception
|
||||
{
|
||||
String label = "Test Pivot Report JSON";
|
||||
QRecord savedReport = insertBasicSavedPivotReport(label);
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.JSON);
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
String json = StringUtils.join("\n", IOUtils.readLines(inputStream, StandardCharsets.UTF_8));
|
||||
printReport(json);
|
||||
|
||||
JSONArray jsonArray = new JSONArray(json);
|
||||
assertEquals(2, jsonArray.length());
|
||||
|
||||
JSONObject firstView = jsonArray.getJSONObject(0);
|
||||
assertEquals(label, firstView.getString("name"));
|
||||
JSONArray firstViewData = firstView.getJSONArray("data");
|
||||
assertEquals(6, firstViewData.length());
|
||||
assertThat(firstViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("id", 1)
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin");
|
||||
|
||||
JSONObject pivotView = jsonArray.getJSONObject(1);
|
||||
assertEquals("Pivot Table", pivotView.getString("name"));
|
||||
JSONArray pivotViewData = pivotView.getJSONArray("data");
|
||||
assertEquals(4, pivotViewData.length());
|
||||
assertThat(pivotViewData.getJSONObject(0).toMap())
|
||||
.hasFieldOrPropertyWithValue("homeState", "IL")
|
||||
.hasFieldOrPropertyWithValue("firstName", "Darin")
|
||||
.hasFieldOrPropertyWithValue("countOfId", 3)
|
||||
.hasFieldOrPropertyWithValue("sumOfCost", new BigDecimal("1.50"));
|
||||
assertThat(pivotViewData.getJSONObject(3).toMap())
|
||||
.hasFieldOrPropertyWithValue("homeState", "Totals")
|
||||
.hasFieldOrPropertyWithValue("countOfId", 6)
|
||||
.hasFieldOrPropertyWithValue("sumOfCost", new BigDecimal("12.00"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void printReport(String report)
|
||||
{
|
||||
// System.out.println(report);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotCSV() throws Exception
|
||||
{
|
||||
String label = "Test Pivot Report CSV";
|
||||
QRecord savedReport = insertBasicSavedPivotReport(label);
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.CSV);
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
List<String> csv = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
|
||||
System.out.println(csv);
|
||||
|
||||
assertEquals("""
|
||||
"Home State","First Name","Count Of Id","Sum Of Cost","Min Of Birth Date","Max Of Birth Date\"""", csv.get(0));
|
||||
|
||||
assertEquals("""
|
||||
"Totals","","6","12.00","1979-12-30","1980-03-20\"""", csv.get(4));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QRecord insertSavedPivotReportWithAllFunctions(String label) throws QException
|
||||
{
|
||||
PivotTableDefinition pivotTableDefinition = new PivotTableDefinition()
|
||||
.withRow(new PivotTableGroupBy().withFieldName("firstName"));
|
||||
|
||||
for(PivotTableFunction function : PivotTableFunction.values())
|
||||
{
|
||||
pivotTableDefinition.withValue(new PivotTableValue().withFieldName("cost").withFunction(function));
|
||||
}
|
||||
|
||||
return new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(new SavedReport()
|
||||
.withLabel(label)
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withColumnsJson(JsonUtils.toJson(new ReportColumns()
|
||||
.withColumn("id")
|
||||
.withColumn("firstName")
|
||||
.withColumn("cost")))
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
.withPivotTableJson(JsonUtils.toJson(pivotTableDefinition))
|
||||
)).getRecords().get(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotXlsxAllFunctions() throws Exception
|
||||
{
|
||||
String label = "Test Pivot Report";
|
||||
QRecord savedReport = insertSavedPivotReportWithAllFunctions(label);
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.XLSX);
|
||||
|
||||
String serverFilePath = runProcessOutput.getValueString("serverFilePath");
|
||||
System.out.println(serverFilePath);
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
writeTmpFileAndOpen(inputStream, ".xlsx");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testPivotCSVAllFunctions() throws Exception
|
||||
{
|
||||
String label = "Test Pivot Report CSV";
|
||||
QRecord savedReport = insertSavedPivotReportWithAllFunctions(label);
|
||||
RunProcessOutput runProcessOutput = runRenderReportProcess(savedReport, ReportFormatPossibleValueEnum.CSV);
|
||||
|
||||
InputStream inputStream = getInputStream(runProcessOutput);
|
||||
List<String> csv = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
|
||||
System.out.println(csv);
|
||||
|
||||
assertEquals("""
|
||||
"First Name","Average Of Cost","Count Of Cost","Count_nums Of Cost","Max Of Cost","Min Of Cost","Product Of Cost","Std_dev Of Cost","Std_devp Of Cost","Sum Of Cost","Var Of Cost","Varp Of Cost\"""", csv.get(0));
|
||||
|
||||
assertEquals("""
|
||||
"Totals","2.0","6","6","3.50","0.50","5.359375000000","1.6432","1.5000","12.00","2.7000","2.2500\"""", csv.get(4));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static RunProcessOutput runRenderReportProcess(QRecord savedReport, ReportFormatPossibleValueEnum reportFormat) throws QException
|
||||
{
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setProcessName(RenderSavedReportMetaDataProducer.NAME);
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
input.setCallback(QProcessCallbackFactory.forRecord(savedReport));
|
||||
input.addValue("reportFormat", reportFormat.getPossibleValueId());
|
||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||
return runProcessOutput;
|
||||
}
|
||||
|
||||
}
|
@ -22,7 +22,6 @@
|
||||
package com.kingsrook.qqq.backend.core.scheduler;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
@ -32,8 +31,6 @@ import com.kingsrook.qqq.backend.core.logging.QCollectingLogger;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJob;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobParameter;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzTestUtils;
|
||||
@ -83,29 +80,6 @@ class QScheduleManagerTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private ScheduledJob newScheduledJob(ScheduledJobType type, Map<String, String> params)
|
||||
{
|
||||
ScheduledJob scheduledJob = new ScheduledJob()
|
||||
.withId(1)
|
||||
.withIsActive(true)
|
||||
.withSchedulerName(TestUtils.SIMPLE_SCHEDULER_NAME)
|
||||
.withType(type.name())
|
||||
.withRepeatSeconds(1)
|
||||
.withJobParameters(new ArrayList<>());
|
||||
|
||||
for(Map.Entry<String, String> entry : params.entrySet())
|
||||
{
|
||||
scheduledJob.getJobParameters().add(new ScheduledJobParameter().withKey(entry.getKey()).withValue(entry.getValue()));
|
||||
}
|
||||
|
||||
return (scheduledJob);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -114,54 +88,54 @@ class QScheduleManagerTest extends BaseTest
|
||||
{
|
||||
QScheduleManager qScheduleManager = QScheduleManager.initInstance(QContext.getQInstance(), () -> QContext.getQSession());
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withRepeatSeconds(null)))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withRepeatSeconds(null)))
|
||||
.hasMessageContaining("Missing a schedule");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withType(null)))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withType(null)))
|
||||
.hasMessageContaining("Missing a type");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withType("notAType")))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of()).withType("notAType")))
|
||||
.hasMessageContaining("Unrecognized type");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of())))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of())))
|
||||
.hasMessageContaining("Missing scheduledJobParameter with key [processName]");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of("processName", "notAProcess"))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of("processName", "notAProcess"))))
|
||||
.hasMessageContaining("Unrecognized processName");
|
||||
|
||||
QContext.getQInstance().getProcess(TestUtils.PROCESS_NAME_BASEPULL).withSchedule(new QScheduleMetaData());
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS, Map.of("processName", TestUtils.PROCESS_NAME_BASEPULL))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS, Map.of("processName", TestUtils.PROCESS_NAME_BASEPULL))))
|
||||
.hasMessageContaining("has a schedule in its metaData - so it should not be dynamically scheduled");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of())))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of())))
|
||||
.hasMessageContaining("Missing scheduledJobParameter with key [queueName]");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of("queueName", "notAQueue"))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of("queueName", "notAQueue"))))
|
||||
.hasMessageContaining("Unrecognized queueName");
|
||||
|
||||
QContext.getQInstance().getQueue(TestUtils.TEST_SQS_QUEUE).withSchedule(new QScheduleMetaData());
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of("queueName", TestUtils.TEST_SQS_QUEUE))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR, Map.of("queueName", TestUtils.TEST_SQS_QUEUE))))
|
||||
.hasMessageContaining("has a schedule in its metaData - so it should not be dynamically scheduled");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of())))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of())))
|
||||
.hasMessageContaining("Missing scheduledJobParameter with key [tableName]");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", "notATable"))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", "notATable"))))
|
||||
.hasMessageContaining("Missing scheduledJobParameter with key [automationStatus]");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", "notATable", "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", "notATable", "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
.hasMessageContaining("Unrecognized tableName");
|
||||
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC, "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_LINE_ITEM_EXTRINSIC, "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
.hasMessageContaining("does not have automationDetails");
|
||||
|
||||
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().withSchedule(null);
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY, "automationStatus", "foobar"))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY, "automationStatus", "foobar"))))
|
||||
.hasMessageContaining("Did not find table automation actions matching automationStatus")
|
||||
.hasMessageContaining("Found: PENDING_INSERT_AUTOMATIONS,PENDING_UPDATE_AUTOMATIONS");
|
||||
|
||||
QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().withSchedule(new QScheduleMetaData());
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY, "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
assertThatThrownBy(() -> qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS, Map.of("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY, "automationStatus", AutomationStatus.PENDING_INSERT_AUTOMATIONS.name()))))
|
||||
.hasMessageContaining("has a schedule in its metaData - so it should not be dynamically scheduled");
|
||||
}
|
||||
|
||||
@ -181,19 +155,19 @@ class QScheduleManagerTest extends BaseTest
|
||||
QScheduleManager qScheduleManager = QScheduleManager.initInstance(qInstance, () -> QContext.getQSession());
|
||||
qScheduleManager.start();
|
||||
|
||||
qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.PROCESS,
|
||||
qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS,
|
||||
Map.of("processName", TestUtils.PROCESS_NAME_GREET_PEOPLE))
|
||||
.withId(2)
|
||||
.withSchedulerName(QuartzTestUtils.QUARTZ_SCHEDULER_NAME));
|
||||
|
||||
qInstance.getQueue(TestUtils.TEST_SQS_QUEUE).setSchedule(null);
|
||||
qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR,
|
||||
qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.QUEUE_PROCESSOR,
|
||||
Map.of("queueName", TestUtils.TEST_SQS_QUEUE))
|
||||
.withId(3)
|
||||
.withSchedulerName(QuartzTestUtils.QUARTZ_SCHEDULER_NAME));
|
||||
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).getAutomationDetails().setSchedule(null);
|
||||
qScheduleManager.setupScheduledJob(newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS,
|
||||
qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.TABLE_AUTOMATIONS,
|
||||
Map.of("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY, "automationStatus", AutomationStatus.PENDING_UPDATE_AUTOMATIONS.name()))
|
||||
.withId(4)
|
||||
.withSchedulerName(QuartzTestUtils.QUARTZ_SCHEDULER_NAME));
|
||||
|
@ -22,7 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.scheduler;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
@ -31,6 +33,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJob;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobParameter;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -57,6 +63,28 @@ public class SchedulerTestUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static ScheduledJob newScheduledJob(ScheduledJobType type, Map<String, String> params)
|
||||
{
|
||||
ScheduledJob scheduledJob = new ScheduledJob()
|
||||
.withId(1)
|
||||
.withIsActive(true)
|
||||
.withSchedulerName(TestUtils.SIMPLE_SCHEDULER_NAME)
|
||||
.withType(type.name())
|
||||
.withRepeatSeconds(1)
|
||||
.withJobParameters(new ArrayList<>());
|
||||
|
||||
for(Map.Entry<String, String> entry : params.entrySet())
|
||||
{
|
||||
scheduledJob.getJobParameters().add(new ScheduledJobParameter().withKey(entry.getKey()).withValue(entry.getValue()));
|
||||
}
|
||||
|
||||
return (scheduledJob);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.scheduler.processes;
|
||||
|
||||
|
||||
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.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.QScheduleManager;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.SchedulerTestUtils;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzJobAndTriggerWrapper;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzTestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.quartz.SchedulerException;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RescheduleAllJobsProcess
|
||||
*******************************************************************************/
|
||||
class RescheduleAllJobsProcessTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@AfterEach
|
||||
void afterEach()
|
||||
{
|
||||
QLogger.deactivateCollectingLoggerForClass(QuartzScheduler.class);
|
||||
|
||||
try
|
||||
{
|
||||
QScheduleManager.getInstance().unInit();
|
||||
}
|
||||
catch(IllegalStateException ise)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// ok, might just mean that this test didn't init the instance //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
QuartzScheduler.getInstance().unInit();
|
||||
}
|
||||
catch(IllegalStateException ise)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// ok, might just mean that this test didn't init the instance //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException, SchedulerException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, RescheduleAllJobsProcess.class.getPackageName());
|
||||
QuartzTestUtils.setupInstanceForQuartzTests();
|
||||
|
||||
QScheduleManager qScheduleManager = QScheduleManager.initInstance(qInstance, () -> QContext.getQSession());
|
||||
qScheduleManager.start();
|
||||
|
||||
qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS,
|
||||
Map.of("processName", TestUtils.PROCESS_NAME_GREET_PEOPLE))
|
||||
.withId(2)
|
||||
.withSchedulerName(QuartzTestUtils.QUARTZ_SCHEDULER_NAME));
|
||||
|
||||
QuartzScheduler quartzScheduler = QuartzScheduler.getInstance();
|
||||
List<QuartzJobAndTriggerWrapper> wrappers = quartzScheduler.queryQuartz();
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// make sure our scheduledJob here got scheduled with quartz //
|
||||
///////////////////////////////////////////////////////////////
|
||||
assertTrue(wrappers.stream().anyMatch(w -> w.jobDetail().getKey().getName().equals("scheduledJob:2")));
|
||||
|
||||
/////////////////////////
|
||||
// run the re-schedule //
|
||||
/////////////////////////
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
input.setProcessName(RescheduleAllJobsProcess.class.getSimpleName());
|
||||
new RunProcessAction().execute(input);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now, because our scheduled job record isn't actually stored in ScheduledJob table, //
|
||||
// when we reschdule all, it should become unscheduled. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
wrappers = quartzScheduler.queryQuartz();
|
||||
assertTrue(wrappers.stream().noneMatch(w -> w.jobDetail().getKey().getName().equals("scheduledJob:2")));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.scheduler.processes;
|
||||
|
||||
|
||||
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.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scheduledjobs.ScheduledJobType;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.QScheduleManager;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.SchedulerTestUtils;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzJobAndTriggerWrapper;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzTestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.quartz.SchedulerException;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for UnscheduleAllJobsProcess
|
||||
*******************************************************************************/
|
||||
class UnscheduleAllJobsProcessTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@AfterEach
|
||||
void afterEach()
|
||||
{
|
||||
QLogger.deactivateCollectingLoggerForClass(QuartzScheduler.class);
|
||||
|
||||
try
|
||||
{
|
||||
QScheduleManager.getInstance().unInit();
|
||||
}
|
||||
catch(IllegalStateException ise)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// ok, might just mean that this test didn't init the instance //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
QuartzScheduler.getInstance().unInit();
|
||||
}
|
||||
catch(IllegalStateException ise)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// ok, might just mean that this test didn't init the instance //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException, SchedulerException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, UnscheduleAllJobsProcess.class.getPackageName());
|
||||
QuartzTestUtils.setupInstanceForQuartzTests();
|
||||
|
||||
QScheduleManager qScheduleManager = QScheduleManager.initInstance(qInstance, () -> QContext.getQSession());
|
||||
qScheduleManager.start();
|
||||
|
||||
qScheduleManager.setupScheduledJob(SchedulerTestUtils.newScheduledJob(ScheduledJobType.PROCESS,
|
||||
Map.of("processName", TestUtils.PROCESS_NAME_GREET_PEOPLE))
|
||||
.withId(2)
|
||||
.withSchedulerName(QuartzTestUtils.QUARTZ_SCHEDULER_NAME));
|
||||
|
||||
QuartzScheduler quartzScheduler = QuartzScheduler.getInstance();
|
||||
List<QuartzJobAndTriggerWrapper> wrappers = quartzScheduler.queryQuartz();
|
||||
assertTrue(wrappers.stream().anyMatch(w -> w.jobDetail().getKey().getName().equals("scheduledJob:2")));
|
||||
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
input.setProcessName(UnscheduleAllJobsProcess.class.getSimpleName());
|
||||
new RunProcessAction().execute(input);
|
||||
|
||||
wrappers = quartzScheduler.queryQuartz();
|
||||
assertTrue(wrappers.stream().noneMatch(w -> w.jobDetail().getKey().getName().equals("scheduledJob:2")));
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,14 @@ public class PersonQRecord extends QRecord
|
||||
|
||||
|
||||
|
||||
public PersonQRecord withFirstName(String firstName)
|
||||
{
|
||||
setValue("firstName", firstName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public PersonQRecord withBirthDate(LocalDate birthDate)
|
||||
{
|
||||
setValue("birthDate", birthDate);
|
||||
|
@ -25,7 +25,9 @@ package com.kingsrook.qqq.backend.core.utils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -79,4 +81,20 @@ class ObjectUtilsTest
|
||||
assertEquals("else", ObjectUtils.tryAndRequireNonNullElse(() -> null, "else"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testIfCan()
|
||||
{
|
||||
Object nullObject = null;
|
||||
assertTrue(ObjectUtils.ifCan(() -> true));
|
||||
assertTrue(ObjectUtils.ifCan(() -> "a".equals("a")));
|
||||
assertFalse(ObjectUtils.ifCan(() -> 1 == 2));
|
||||
assertFalse(ObjectUtils.ifCan(() -> nullObject.equals("a")));
|
||||
assertFalse(ObjectUtils.ifCan(() -> null));
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,9 @@ package com.kingsrook.qqq.backend.core.utils.aggregates;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import org.assertj.core.data.Offset;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -78,6 +81,12 @@ class AggregatesTest extends BaseTest
|
||||
assertEquals(15, aggregates.getMax());
|
||||
assertEquals(30, aggregates.getSum());
|
||||
assertThat(aggregates.getAverage()).isCloseTo(new BigDecimal("10"), Offset.offset(BigDecimal.ZERO));
|
||||
|
||||
assertEquals(new BigDecimal("750"), aggregates.getProduct());
|
||||
assertEquals(new BigDecimal("25.0000"), aggregates.getVariance());
|
||||
assertEquals(new BigDecimal("5.0000"), aggregates.getStandardDeviation());
|
||||
assertThat(aggregates.getVarP()).isCloseTo(new BigDecimal("16.6667"), Offset.offset(new BigDecimal(".0001")));
|
||||
assertThat(aggregates.getStdDevP()).isCloseTo(new BigDecimal("4.0824"), Offset.offset(new BigDecimal(".0001")));
|
||||
}
|
||||
|
||||
|
||||
@ -89,6 +98,7 @@ class AggregatesTest extends BaseTest
|
||||
void testBigDecimal()
|
||||
{
|
||||
BigDecimalAggregates aggregates = new BigDecimalAggregates();
|
||||
aggregates.add(null);
|
||||
|
||||
assertEquals(0, aggregates.getCount());
|
||||
assertNull(aggregates.getMin());
|
||||
@ -114,13 +124,117 @@ class AggregatesTest extends BaseTest
|
||||
|
||||
BigDecimal bd148 = new BigDecimal("14.8");
|
||||
aggregates.add(bd148);
|
||||
|
||||
aggregates.add(null);
|
||||
assertEquals(3, aggregates.getCount());
|
||||
assertEquals(bd51, aggregates.getMin());
|
||||
assertEquals(bd148, aggregates.getMax());
|
||||
assertEquals(new BigDecimal("30.0"), aggregates.getSum());
|
||||
assertThat(aggregates.getAverage()).isCloseTo(new BigDecimal("10.0"), Offset.offset(BigDecimal.ZERO));
|
||||
|
||||
assertEquals(new BigDecimal("762.348"), aggregates.getProduct());
|
||||
assertEquals(new BigDecimal("23.5300"), aggregates.getVariance());
|
||||
assertEquals(new BigDecimal("4.8508"), aggregates.getStandardDeviation());
|
||||
assertThat(aggregates.getVarP()).isCloseTo(new BigDecimal("15.6867"), Offset.offset(new BigDecimal(".0001")));
|
||||
assertThat(aggregates.getStdDevP()).isCloseTo(new BigDecimal("3.9606"), Offset.offset(new BigDecimal(".0001")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testInstant()
|
||||
{
|
||||
InstantAggregates aggregates = new InstantAggregates();
|
||||
|
||||
assertEquals(0, aggregates.getCount());
|
||||
assertNull(aggregates.getMin());
|
||||
assertNull(aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertNull(aggregates.getAverage());
|
||||
|
||||
Instant i1970 = Instant.parse("1970-01-01T00:00:00Z");
|
||||
aggregates.add(i1970);
|
||||
assertEquals(1, aggregates.getCount());
|
||||
assertEquals(i1970, aggregates.getMin());
|
||||
assertEquals(i1970, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(i1970, aggregates.getAverage());
|
||||
|
||||
Instant i1980 = Instant.parse("1980-01-01T00:00:00Z");
|
||||
aggregates.add(i1980);
|
||||
assertEquals(2, aggregates.getCount());
|
||||
assertEquals(i1970, aggregates.getMin());
|
||||
assertEquals(i1980, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(Instant.parse("1975-01-01T00:00:00Z"), aggregates.getAverage());
|
||||
|
||||
Instant i1990 = Instant.parse("1990-01-01T00:00:00Z");
|
||||
aggregates.add(i1990);
|
||||
assertEquals(3, aggregates.getCount());
|
||||
assertEquals(i1970, aggregates.getMin());
|
||||
assertEquals(i1990, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(Instant.parse("1980-01-01T08:00:00Z"), aggregates.getAverage()); // a leap day throws this off by 8 hours :)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// assert we gracefully return null for these ops we don't support //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
assertNull(aggregates.getProduct());
|
||||
assertNull(aggregates.getVariance());
|
||||
assertNull(aggregates.getStandardDeviation());
|
||||
assertNull(aggregates.getVarP());
|
||||
assertNull(aggregates.getStdDevP());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testLocalDate()
|
||||
{
|
||||
LocalDateAggregates aggregates = new LocalDateAggregates();
|
||||
|
||||
assertEquals(0, aggregates.getCount());
|
||||
assertNull(aggregates.getMin());
|
||||
assertNull(aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertNull(aggregates.getAverage());
|
||||
|
||||
LocalDate ld1970 = LocalDate.of(1970, Month.JANUARY, 1);
|
||||
aggregates.add(ld1970);
|
||||
assertEquals(1, aggregates.getCount());
|
||||
assertEquals(ld1970, aggregates.getMin());
|
||||
assertEquals(ld1970, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(ld1970, aggregates.getAverage());
|
||||
|
||||
LocalDate ld1980 = LocalDate.of(1980, Month.JANUARY, 1);
|
||||
aggregates.add(ld1980);
|
||||
assertEquals(2, aggregates.getCount());
|
||||
assertEquals(ld1970, aggregates.getMin());
|
||||
assertEquals(ld1980, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(LocalDate.of(1975, Month.JANUARY, 1), aggregates.getAverage());
|
||||
|
||||
LocalDate ld1990 = LocalDate.of(1990, Month.JANUARY, 1);
|
||||
aggregates.add(ld1990);
|
||||
assertEquals(3, aggregates.getCount());
|
||||
assertEquals(ld1970, aggregates.getMin());
|
||||
assertEquals(ld1990, aggregates.getMax());
|
||||
assertNull(aggregates.getSum());
|
||||
assertEquals(ld1980, aggregates.getAverage());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// assert we gracefully return null for these ops we don't support //
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
assertNull(aggregates.getProduct());
|
||||
assertNull(aggregates.getVariance());
|
||||
assertNull(aggregates.getStandardDeviation());
|
||||
assertNull(aggregates.getVarP());
|
||||
assertNull(aggregates.getStdDevP());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user