diff --git a/ui/src/components/executions/ExecutionOutput.vue b/ui/src/components/executions/ExecutionOutput.vue
new file mode 100644
index 0000000000..392af7d60e
--- /dev/null
+++ b/ui/src/components/executions/ExecutionOutput.vue
@@ -0,0 +1,69 @@
+
+
+
+ {{$t('outputs') | cap}}
+
+
+ {{$t('download') | cap}}
+ {{row.item.value}}
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/src/components/executions/ExecutionRoot.vue b/ui/src/components/executions/ExecutionRoot.vue
index a824a4a366..0990bda65b 100644
--- a/ui/src/components/executions/ExecutionRoot.vue
+++ b/ui/src/components/executions/ExecutionRoot.vue
@@ -20,6 +20,7 @@ import Gantt from "./Gantt";
import Overview from "./Overview";
import Logs from "./Logs";
import Topology from "./Topology";
+import ExecutionOutput from "./ExecutionOutput";
import Trigger from "vue-material-design-icons/Cogs";
import BottomLine from "../layout/BottomLine";
import FlowActions from "../flows/FlowActions";
@@ -35,7 +36,8 @@ export default {
Gantt,
Logs,
Topology,
- FlowActions
+ FlowActions,
+ ExecutionOutput
},
data() {
return {
@@ -122,6 +124,10 @@ export default {
{
tab: "topology",
title: title("topology")
+ },
+ {
+ tab: "execution-output",
+ title: title("output")
}
];
}
diff --git a/ui/src/components/executions/Overview.vue b/ui/src/components/executions/Overview.vue
index d894e71cd5..d152e9f868 100644
--- a/ui/src/components/executions/Overview.vue
+++ b/ui/src/components/executions/Overview.vue
@@ -5,13 +5,40 @@
+ {{$t('execution') | cap}}
+
+
+ {{$t('inputs') | cap}}
+
+
+ {{row.item.value}}
+ {{$t('download') | cap}}
+
+
+
\ No newline at end of file
diff --git a/ui/src/http.js b/ui/src/http.js
index 2b82945836..7d8abf09b1 100644
--- a/ui/src/http.js
+++ b/ui/src/http.js
@@ -16,3 +16,6 @@ export default callback => {
Vue.axios.defaults.baseURL = (process.env.VUE_APP_API_URL || "") + "/";
callback();
};
+
+
+export const apiRoot = `${process.env.VUE_APP_API_URL}/api/v1/`
\ No newline at end of file
diff --git a/webserver/src/main/java/org/kestra/webserver/controllers/ExecutionController.java b/webserver/src/main/java/org/kestra/webserver/controllers/ExecutionController.java
index 74724c8162..68eccfb176 100644
--- a/webserver/src/main/java/org/kestra/webserver/controllers/ExecutionController.java
+++ b/webserver/src/main/java/org/kestra/webserver/controllers/ExecutionController.java
@@ -1,18 +1,22 @@
package org.kestra.webserver.controllers;
import io.micronaut.data.model.Pageable;
+import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
+import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.annotation.QueryValue;
import io.micronaut.http.exceptions.HttpStatusException;
import io.micronaut.http.multipart.StreamingFileUpload;
+import io.micronaut.http.server.types.files.StreamedFile;
import io.micronaut.http.sse.Event;
import io.micronaut.validation.Validated;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
+import org.apache.commons.io.FilenameUtils;
import org.kestra.core.models.executions.Execution;
import org.kestra.core.models.flows.Flow;
import org.kestra.core.queues.QueueFactoryInterface;
@@ -20,6 +24,8 @@
import org.kestra.core.repositories.ExecutionRepositoryInterface;
import org.kestra.core.repositories.FlowRepositoryInterface;
import org.kestra.core.runners.RunnerUtils;
+import org.kestra.core.storages.StorageInterface;
+import org.kestra.core.storages.StorageObject;
import org.kestra.webserver.responses.PagedResults;
import org.kestra.webserver.utils.PageableUtils;
import org.reactivestreams.Publisher;
@@ -27,6 +33,11 @@
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -45,6 +56,9 @@ public class ExecutionController {
@Inject
private RunnerUtils runnerUtils;
+ @Inject
+ private StorageInterface storageInterface;
+
@Inject
@Named(QueueFactoryInterface.EXECUTION_NAMED)
protected QueueInterface executionQueue;
@@ -126,6 +140,30 @@ public Maybe trigger(
return Maybe.just(current);
}
+ /**
+ * Download file binary from uri parameter
+ *
+ * @param filePath The file URI to return
+ * @param type The file storage type
+ * @return data binary content
+ */
+ @Get(uri = "executions/{executionId}/file", produces = MediaType.APPLICATION_OCTET_STREAM)
+ public Maybe file(
+ String executionId,
+ @QueryValue(value = "filePath") URI filePath,
+ @QueryValue(value = "type") String type
+ ) throws URISyntaxException, IOException {
+ Optional execution = executionRepository.findById(executionId);
+ if (execution.isEmpty()) {
+ return Maybe.empty();
+ }
+
+ InputStream fileHandler = storageInterface.get(filePath);
+ return Maybe.just(new StreamedFile(fileHandler, MediaType.APPLICATION_OCTET_STREAM_TYPE)
+ .attach(FilenameUtils.getName(filePath.toString()))
+ );
+ }
+
/**
* Trigger an new execution for current flow and follow execution
*