diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/implementations/savedreports/RenderSavedReportProcessApiProcessOutput.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/implementations/savedreports/RenderSavedReportProcessApiProcessOutput.java index e8d698b0..435353b0 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/implementations/savedreports/RenderSavedReportProcessApiProcessOutput.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/implementations/savedreports/RenderSavedReportProcessApiProcessOutput.java @@ -22,20 +22,19 @@ package com.kingsrook.qqq.api.implementations.savedreports; -import java.io.File; import java.io.Serializable; -import java.nio.charset.Charset; import java.util.Map; import com.kingsrook.qqq.api.model.actions.HttpApiResponse; import com.kingsrook.qqq.api.model.metadata.processes.ApiProcessOutputInterface; import com.kingsrook.qqq.api.model.openapi.Content; import com.kingsrook.qqq.api.model.openapi.Response; import com.kingsrook.qqq.api.model.openapi.Schema; +import com.kingsrook.qqq.backend.core.actions.tables.StorageAction; 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.ReportFormat; -import org.apache.commons.io.FileUtils; +import com.kingsrook.qqq.backend.core.model.actions.tables.storage.StorageInput; import org.eclipse.jetty.http.HttpStatus; @@ -51,25 +50,10 @@ public class RenderSavedReportProcessApiProcessOutput implements ApiProcessOutpu @Override public Serializable getOutputForProcess(RunProcessInput runProcessInput, RunProcessOutput runProcessOutput) throws QException { - try - { - ReportFormat reportFormat = ReportFormat.fromString(runProcessOutput.getValueString("reportFormat")); - - String filePath = runProcessOutput.getValueString("serverFilePath"); - File file = new File(filePath); - if(reportFormat.getIsBinary()) - { - return FileUtils.readFileToByteArray(file); - } - else - { - return FileUtils.readFileToString(file, Charset.defaultCharset()); - } - } - catch(Exception e) - { - throw new QException("Error streaming report contents", e); - } + ////////////////////////////////////////////////////////////////// + // we don't use output like this - see customizeHttpApiResponse // + ////////////////////////////////////////////////////////////////// + return (null); } @@ -85,8 +69,18 @@ public class RenderSavedReportProcessApiProcessOutput implements ApiProcessOutpu ///////////////////////////////////////////////////////////////////////////////////////////// httpApiResponse.setNeedsFormattedAsJson(false); + ///////////////////////////////////////////// + // set content type based on report format // + ///////////////////////////////////////////// ReportFormat reportFormat = ReportFormat.fromString(runProcessOutput.getValueString("reportFormat")); httpApiResponse.setContentType(reportFormat.getMimeType()); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // get an input stream from the backend where the report content is stored - send that down to the caller // + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + String storageTableName = runProcessOutput.getValueString("storageTableName"); + String storageReference = runProcessOutput.getValueString("storageReference"); + httpApiResponse.setInputStream(new StorageAction().getInputStream(new StorageInput(storageTableName).withReference(storageReference))); } diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandler.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandler.java index f3c5842e..99951958 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandler.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/javalin/QJavalinApiHandler.java @@ -380,25 +380,36 @@ public class QJavalinApiHandler context.contentType(response.getContentType()); } - //////////////////////////////////////////////////////////////////////////////////// - // else, try to return it raw - as byte[], or String, or as a converted-to-String // - //////////////////////////////////////////////////////////////////////////////////// - Serializable result = Objects.requireNonNullElse(response.getResponseBodyObject(), ""); - if(result instanceof byte[] ba) + /////////////////////////////////////////////////////////////////////////////////// + // if there's an input stream in the response, just send that down to the client // + /////////////////////////////////////////////////////////////////////////////////// + if(response.getInputStream() != null) { - context.result(ba); - storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody("Byte array of length: " + ba.length)); - } - else if(result instanceof String s) - { - context.result(s); - storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(s)); + context.result(response.getInputStream()); + storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody("Streamed result")); } else { - String resultString = String.valueOf(result); - context.result(resultString); - storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString)); + //////////////////////////////////////////////////////////////////////////////////// + // else, try to return it raw - as byte[], or String, or as a converted-to-String // + //////////////////////////////////////////////////////////////////////////////////// + Serializable result = Objects.requireNonNullElse(response.getResponseBodyObject(), ""); + if(result instanceof byte[] ba) + { + context.result(ba); + storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody("Byte array of length: " + ba.length)); + } + else if(result instanceof String s) + { + context.result(s); + storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(s)); + } + else + { + String resultString = String.valueOf(result); + context.result(resultString); + storeApiLog(apiLog.withStatusCode(context.statusCode()).withResponseBody(resultString)); + } } } } diff --git a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/model/actions/HttpApiResponse.java b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/model/actions/HttpApiResponse.java index b255c042..8bedcda0 100644 --- a/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/model/actions/HttpApiResponse.java +++ b/qqq-middleware-api/src/main/java/com/kingsrook/qqq/api/model/actions/HttpApiResponse.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.api.model.actions; +import java.io.InputStream; import java.io.Serializable; import org.eclipse.jetty.http.HttpStatus; @@ -37,6 +38,8 @@ public class HttpApiResponse private String contentType; + private InputStream inputStream; + //////////////////////////////////////////////////////////////////////////////////////////////////////// // by default - QJavalinApiHandler will format the responseBodyObject as JSON. // // set this field to false if you don't want it to do that (e.g., if your response is, say, a byte[]) // @@ -189,4 +192,35 @@ public class HttpApiResponse return (this); } + + /******************************************************************************* + ** Getter for inputStream + *******************************************************************************/ + public InputStream getInputStream() + { + return (this.inputStream); + } + + + + /******************************************************************************* + ** Setter for inputStream + *******************************************************************************/ + public void setInputStream(InputStream inputStream) + { + this.inputStream = inputStream; + } + + + + /******************************************************************************* + ** Fluent setter for inputStream + *******************************************************************************/ + public HttpApiResponse withInputStream(InputStream inputStream) + { + this.inputStream = inputStream; + return (this); + } + + } \ No newline at end of file diff --git a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/TestUtils.java b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/TestUtils.java index 4dc34bf4..66cb753c 100644 --- a/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/TestUtils.java +++ b/qqq-middleware-api/src/test/java/com/kingsrook/qqq/api/TestUtils.java @@ -175,7 +175,7 @@ public class TestUtils private static void addSavedReports(QInstance qInstance) throws QException { qInstance.add(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(qInstance)); - new SavedReportsMetaDataProvider().defineAll(qInstance, MEMORY_BACKEND_NAME, null); + new SavedReportsMetaDataProvider().defineAll(qInstance, MEMORY_BACKEND_NAME, MEMORY_BACKEND_NAME, null); RenderSavedReportProcessApiMetaDataEnricher.setupProcessForApi(qInstance.getProcess(RenderSavedReportMetaDataProducer.NAME), API_NAME, V2022_Q4); }