mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
CE-881 - Update render saved report to take in ReportFormat PossibleValue (new related enum); add first test on same process
This commit is contained in:
@ -26,8 +26,15 @@ import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
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.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -65,4 +72,36 @@ public class QProcessCallbackFactory
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QProcessCallback forRecordEntity(QRecordEntity entity)
|
||||
{
|
||||
return forRecord(entity.toQRecord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QProcessCallback forRecord(QRecord record)
|
||||
{
|
||||
String primaryKeyField = "id";
|
||||
if(StringUtils.hasContent(record.getTableName()))
|
||||
{
|
||||
primaryKeyField = QContext.getQInstance().getTable(record.getTableName()).getPrimaryKeyField();
|
||||
}
|
||||
|
||||
Serializable primaryKeyValue = record.getValue(primaryKeyField);
|
||||
if(primaryKeyValue == null)
|
||||
{
|
||||
throw (new QRuntimeException("Record did not have value in its priary key field [" + primaryKeyField + "]"));
|
||||
}
|
||||
|
||||
return (forFilter(new QQueryFilter().withCriteria(new QFilterCriteria(primaryKeyField, QCriteriaOperator.EQUALS, primaryKeyValue))));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,21 +39,22 @@ import org.dhatim.fastexcel.Worksheet;
|
||||
*******************************************************************************/
|
||||
public enum ReportFormat
|
||||
{
|
||||
XLSX(Worksheet.MAX_ROWS, Worksheet.MAX_COLS, ExcelPoiBasedStreamingExportStreamer::new, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
|
||||
XLSX(Worksheet.MAX_ROWS, Worksheet.MAX_COLS, ExcelPoiBasedStreamingExportStreamer::new, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx"),
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// if we need to fall back to Fastexcel, this was its version of this. //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// XLSX(Worksheet.MAX_ROWS, Worksheet.MAX_COLS, ExcelFastexcelExportStreamer::new, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
|
||||
// XLSX(Worksheet.MAX_ROWS, Worksheet.MAX_COLS, ExcelFastexcelExportStreamer::new, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx"),
|
||||
|
||||
JSON(null, null, JsonExportStreamer::new, "application/json"),
|
||||
CSV(null, null, CsvExportStreamer::new, "text/csv"),
|
||||
LIST_OF_MAPS(null, null, ListOfMapsExportStreamer::new, null);
|
||||
JSON(null, null, JsonExportStreamer::new, "application/json", "json"),
|
||||
CSV(null, null, CsvExportStreamer::new, "text/csv", "csv"),
|
||||
LIST_OF_MAPS(null, null, ListOfMapsExportStreamer::new, null, null);
|
||||
|
||||
|
||||
private final Integer maxRows;
|
||||
private final Integer maxCols;
|
||||
private final String mimeType;
|
||||
private final String extension;
|
||||
|
||||
private final Supplier<? extends ExportStreamerInterface> streamerConstructor;
|
||||
|
||||
@ -62,12 +63,13 @@ public enum ReportFormat
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
ReportFormat(Integer maxRows, Integer maxCols, Supplier<? extends ExportStreamerInterface> streamerConstructor, String mimeType)
|
||||
ReportFormat(Integer maxRows, Integer maxCols, Supplier<? extends ExportStreamerInterface> streamerConstructor, String mimeType, String extension)
|
||||
{
|
||||
this.maxRows = maxRows;
|
||||
this.maxCols = maxCols;
|
||||
this.mimeType = mimeType;
|
||||
this.streamerConstructor = streamerConstructor;
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
|
||||
@ -134,4 +136,15 @@ public enum ReportFormat
|
||||
{
|
||||
return (streamerConstructor.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for extension
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getExtension()
|
||||
{
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.actions.reporting;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** sub-set of ReportFormats to expose as possible-values in-apps
|
||||
*******************************************************************************/
|
||||
public enum ReportFormatPossibleValueEnum implements PossibleValueEnum<String>
|
||||
{
|
||||
XLSX,
|
||||
JSON,
|
||||
CSV;
|
||||
|
||||
public static final String NAME = "reportFormat";
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueId()
|
||||
{
|
||||
return name();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getPossibleValueLabel()
|
||||
{
|
||||
return name();
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.savedreports;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormatPossibleValueEnum;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
@ -49,7 +50,8 @@ public class SavedReportsMetaDataProvider
|
||||
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
instance.addTable(defineSavedReportTable(backendName, backendDetailEnricher));
|
||||
instance.addPossibleValueSource(defineSavedReportPossibleValueSource());
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForTable(SavedReport.TABLE_NAME));
|
||||
instance.addPossibleValueSource(QPossibleValueSource.newForEnum(ReportFormatPossibleValueEnum.NAME, ReportFormatPossibleValueEnum.values()));
|
||||
instance.addProcess(new RenderSavedReportMetaDataProducer().produce(instance));
|
||||
}
|
||||
|
||||
@ -88,14 +90,4 @@ public class SavedReportsMetaDataProvider
|
||||
return (table);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QPossibleValueSource defineSavedReportPossibleValueSource()
|
||||
{
|
||||
return QPossibleValueSource.newForTable(SavedReport.TABLE_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.GenerateReportAction;
|
||||
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.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportDestination;
|
||||
@ -47,6 +48,9 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
*******************************************************************************/
|
||||
public class RenderSavedReportExecuteStep implements BackendStep
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RenderSavedReportExecuteStep.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -56,8 +60,10 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
||||
{
|
||||
try
|
||||
{
|
||||
ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString("reportFormat"));
|
||||
|
||||
SavedReport savedReport = new SavedReport(runBackendStepInput.getRecords().get(0));
|
||||
File tmpFile = File.createTempFile("SavedReport" + savedReport.getId(), ".xlsx", new File("/tmp/"));
|
||||
File tmpFile = File.createTempFile("SavedReport" + savedReport.getId(), "." + reportFormat.getExtension(), new File("/tmp/"));
|
||||
|
||||
runBackendStepInput.getAsyncJobCallback().updateStatus("Generating Report");
|
||||
|
||||
@ -68,7 +74,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportMetaData(reportMetaData);
|
||||
reportInput.setReportDestination(new ReportDestination()
|
||||
.withReportFormat(ReportFormat.XLSX) // todo - variable
|
||||
.withReportFormat(reportFormat)
|
||||
.withReportOutputStream(reportOutputStream));
|
||||
|
||||
Map<String, Serializable> values = runBackendStepInput.getValues();
|
||||
@ -78,12 +84,14 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
||||
}
|
||||
|
||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport);
|
||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
|
||||
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + "." + reportFormat.getExtension());
|
||||
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// todo - render error screen?
|
||||
|
||||
LOG.warn("Error rendering saved report", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,11 @@ package com.kingsrook.qqq.backend.core.processes.implementations.savedreports;
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormatPossibleValueEnum;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||
@ -45,6 +48,7 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
|
||||
public static final String NAME = "renderSavedReport";
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -62,7 +66,10 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
|
||||
.withCode(new QCodeReference(RenderSavedReportPreStep.class)))
|
||||
.addStep(new QFrontendStepMetaData()
|
||||
.withName("input")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM)))
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
|
||||
.withFormField(new QFieldMetaData("reportFormat", QFieldType.STRING)
|
||||
.withPossibleValueSourceName(ReportFormatPossibleValueEnum.NAME)
|
||||
.withIsRequired(true)))
|
||||
.addStep(new QBackendStepMetaData()
|
||||
.withName("execute")
|
||||
.withInputData(new QFunctionInputMetaData().withRecordListMetaData(new QRecordListMetaData()
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.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.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.tables.insert.InsertInput;
|
||||
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.savedreports.SavedReport;
|
||||
import com.kingsrook.qqq.backend.core.model.savedreports.SavedReportsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.LocalMacDevUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
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.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RenderSavedReportExecuteStep
|
||||
*******************************************************************************/
|
||||
class RenderSavedReportProcessTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws Exception
|
||||
{
|
||||
new SavedReportsMetaDataProvider().defineAll(QContext.getQInstance(), TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String label = "Test Report";
|
||||
|
||||
QRecord savedReport = new InsertAction().execute(new InsertInput(SavedReport.TABLE_NAME).withRecordEntity(new SavedReport()
|
||||
.withLabel(label)
|
||||
.withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withColumnsJson("""
|
||||
{
|
||||
"columns":
|
||||
[
|
||||
{"name": "id"},
|
||||
{"name": "firstName"},
|
||||
{"name": "lastName"}
|
||||
]
|
||||
}
|
||||
""")
|
||||
.withQueryFilterJson(JsonUtils.toJson(new QQueryFilter()))
|
||||
)).getRecords().get(0);
|
||||
|
||||
GenerateReportActionTest.insertPersonRecords(QContext.getQInstance());
|
||||
|
||||
RunProcessInput input = new RunProcessInput();
|
||||
input.setProcessName(RenderSavedReportMetaDataProducer.NAME);
|
||||
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
|
||||
input.setCallback(QProcessCallbackFactory.forRecord(savedReport));
|
||||
input.addValue("reportFormat", ReportFormatPossibleValueEnum.CSV.getPossibleValueId());
|
||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(input);
|
||||
|
||||
String downloadFileName = runProcessOutput.getValueString("downloadFileName");
|
||||
String serverFilePath = runProcessOutput.getValueString("serverFilePath");
|
||||
|
||||
assertThat(downloadFileName)
|
||||
.startsWith(label + " - ")
|
||||
.matches(".*\\d\\d\\d\\d-\\d\\d-\\d\\d-\\d\\d\\d\\d.*")
|
||||
.endsWith(".csv");
|
||||
|
||||
File serverFile = new File(serverFilePath);
|
||||
assertTrue(serverFile.exists());
|
||||
|
||||
List<String> lines = FileUtils.readLines(serverFile);
|
||||
assertEquals("""
|
||||
"Id","First Name","Last Name"
|
||||
""".trim(), lines.get(0));
|
||||
assertEquals("""
|
||||
"1","Darin","Jonson"
|
||||
""".trim(), lines.get(1));
|
||||
|
||||
LocalMacDevUtils.openFile(serverFilePath);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user