mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-25 11:12:12 -05:00
Compare commits
7 Commits
executor_v
...
v0.13.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97a6afbe2e | ||
|
|
cc800b4528 | ||
|
|
da50cf4287 | ||
|
|
1ecfed1559 | ||
|
|
4a3e250019 | ||
|
|
2917c178a5 | ||
|
|
0ceaeb4a97 |
@@ -107,7 +107,6 @@ public class NamespaceFilesService {
|
||||
private void copy(String tenantId, String namespace, Path basePath, List<URI> files) throws IOException {
|
||||
files
|
||||
.forEach(throwConsumer(f -> {
|
||||
InputStream inputStream = storageInterface.get(tenantId, uri(namespace, f));
|
||||
Path destination = Paths.get(basePath.toString(), f.getPath());
|
||||
|
||||
if (!destination.getParent().toFile().exists()) {
|
||||
@@ -115,7 +114,9 @@ public class NamespaceFilesService {
|
||||
destination.getParent().toFile().mkdirs();
|
||||
}
|
||||
|
||||
Files.copy(inputStream, destination);
|
||||
try (InputStream inputStream = storageInterface.get(tenantId, uri(namespace, f))) {
|
||||
Files.copy(inputStream, destination);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.kestra.core.runners.pebble.functions;
|
||||
|
||||
import io.kestra.core.storages.StorageInterface;
|
||||
import io.kestra.core.tenant.TenantService;
|
||||
import io.kestra.core.utils.Slugify;
|
||||
import io.pebbletemplates.pebble.error.PebbleException;
|
||||
import io.pebbletemplates.pebble.extension.Function;
|
||||
@@ -11,6 +10,7 @@ import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
@@ -47,7 +47,9 @@ public class ReadFileFunction implements Function {
|
||||
private String readFromNamespaceFile(EvaluationContext context, String path) throws IOException {
|
||||
Map<String, String> flow = (Map<String, String>) context.getVariable("flow");
|
||||
URI namespaceFile = URI.create(storageInterface.namespaceFilePrefix(flow.get("namespace")) + "/" + path);
|
||||
return new String(storageInterface.get(flow.get("tenantId"), namespaceFile).readAllBytes(), StandardCharsets.UTF_8);
|
||||
try (InputStream inputStream = storageInterface.get(flow.get("tenantId"), namespaceFile)) {
|
||||
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private String readFromInternalStorageUri(EvaluationContext context, String path) throws IOException {
|
||||
@@ -69,7 +71,9 @@ public class ReadFileFunction implements Function {
|
||||
}
|
||||
}
|
||||
URI internalStorageFile = URI.create(path);
|
||||
return new String(storageInterface.get(flow.get("tenantId"), internalStorageFile).readAllBytes(), StandardCharsets.UTF_8);
|
||||
try (InputStream inputStream = storageInterface.get(flow.get("tenantId"), internalStorageFile)) {
|
||||
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validateFileUri(String namespace, String flowId, String executionId, String path) {
|
||||
|
||||
@@ -4,10 +4,7 @@ import io.kestra.core.runners.RunContext;
|
||||
import io.kestra.core.storages.StorageSplitInterface;
|
||||
import io.micronaut.core.convert.format.ReadableBytesTypeConverter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@@ -42,8 +42,7 @@ public interface StorageInterface {
|
||||
* @return true if the uri points to a file/object that exist in the internal storage.
|
||||
*/
|
||||
default boolean exists(String tenantId, URI uri) {
|
||||
try {
|
||||
get(tenantId, uri);
|
||||
try (InputStream ignored = get(tenantId, uri)){
|
||||
return true;
|
||||
} catch (IOException ieo) {
|
||||
return false;
|
||||
|
||||
@@ -54,9 +54,9 @@ public abstract class AbstractState extends Task {
|
||||
|
||||
|
||||
protected Map<String, Object> get(RunContext runContext) throws IllegalVariableEvaluationException, IOException {
|
||||
InputStream taskStateFile = runContext.getTaskStateFile("tasks-states", runContext.render(this.name), this.namespace, this.taskrunValue);
|
||||
|
||||
return JacksonMapper.ofJson(false).readValue(taskStateFile, TYPE_REFERENCE);
|
||||
try (InputStream taskStateFile = runContext.getTaskStateFile("tasks-states", runContext.render(this.name), this.namespace, this.taskrunValue)) {
|
||||
return JacksonMapper.ofJson(false).readValue(taskStateFile, TYPE_REFERENCE);
|
||||
}
|
||||
}
|
||||
|
||||
protected Pair<URI, Map<String, Object>> merge(RunContext runContext, Map<String, Object> map) throws IllegalVariableEvaluationException, IOException {
|
||||
|
||||
@@ -16,6 +16,7 @@ import io.kestra.core.runners.RunContext;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
@@ -128,7 +129,9 @@ public class Concat extends Task implements RunnableTask<Concat.Output> {
|
||||
|
||||
finalFiles.forEach(throwConsumer(s -> {
|
||||
URI from = new URI(runContext.render(s));
|
||||
IOUtils.copyLarge(runContext.uriToInputStream(from), fileOutputStream);
|
||||
try (InputStream inputStream = runContext.uriToInputStream(from)) {
|
||||
IOUtils.copyLarge(inputStream, fileOutputStream);
|
||||
}
|
||||
|
||||
if (separator != null) {
|
||||
IOUtils.copy(new ByteArrayInputStream(this.separator.getBytes()), fileOutputStream);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version=0.13.0
|
||||
version=0.13.1
|
||||
|
||||
jacksonVersion=2.15.2
|
||||
micronautVersion=3.10.1
|
||||
|
||||
@@ -30,6 +30,31 @@ const extensionsToFetch = Object.entries(versionByExtensionIdToFetch).map(([exte
|
||||
}));
|
||||
|
||||
// used to configure VSCode startup
|
||||
const sidebarTabs = [
|
||||
{"id": "workbench.view.explorer", "pinned": true, "visible": true, "order": 0},
|
||||
{"id": "workbench.view.search", "pinned": true, "visible": true, "order": 1},
|
||||
{"id": "workbench.view.scm", "pinned": false, "visible": false, "order": 2},
|
||||
{"id": "workbench.view.debug", "pinned": false,"visible": false,"order": 3},
|
||||
{"id": "workbench.view.extensions", "pinned": true,"visible": true,"order": 4},
|
||||
{"id": "workbench.view.remote", "pinned": false,"visible": false,"order": 4},
|
||||
{"id": "workbench.view.extension.test", "pinned": false,"visible": false,"order": 6},
|
||||
{"id": "workbench.view.extension.references-view", "pinned": false,"visible": false,"order": 7},
|
||||
{"id": "workbench.panel.chatSidebar", "pinned": false,"visible": false,"order": 100},
|
||||
{"id": "userDataProfiles", "pinned": false, "visible": false},
|
||||
{"id": "workbench.view.sync", "pinned": false,"visible": false},
|
||||
{"id": "workbench.view.editSessions", "pinned": false, "visible": false}
|
||||
];
|
||||
|
||||
const bottomBarTabs = [
|
||||
{"id":"workbench.panel.markers", "pinned": false,"visible": false,"order": 0},
|
||||
{"id":"workbench.panel.output", "pinned": false,"visible": false,"order": 1},
|
||||
{"id":"workbench.panel.repl", "pinned": false,"visible": false,"order": 2},
|
||||
{"id":"terminal", "pinned": false,"visible": false,"order": 3},
|
||||
{"id":"workbench.panel.testResults", "pinned": false,"visible": false,"order": 3},
|
||||
{"id":"~remote.forwardedPortsContainer", "pinned": false,"visible": false,"order": 5},
|
||||
{"id":"refactorPreview", "pinned": false,"visible": false}
|
||||
];
|
||||
|
||||
window.product = {
|
||||
productConfiguration: {
|
||||
nameShort: "Kestra VSCode",
|
||||
@@ -77,5 +102,17 @@ window.product = {
|
||||
"workbench.colorTheme": THEME === "dark" ? "Sweet Dracula" : "Default Light Modern",
|
||||
// provide the Kestra root URL to extension
|
||||
"kestra.api.url": KESTRA_API_URL
|
||||
},
|
||||
profile: {
|
||||
name: "Kestra VSCode",
|
||||
contents: JSON.stringify({
|
||||
globalState: JSON.stringify({
|
||||
"storage": {
|
||||
"workbench.activity.pinnedViewlets2": sidebarTabs,
|
||||
"workbench.activity.showAccounts": "false",
|
||||
"workbench.panel.pinnedPanels": bottomBarTabs
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -142,7 +142,7 @@
|
||||
executionId: this.executionId,
|
||||
path: this.value,
|
||||
maxRows: this.maxPreview,
|
||||
encoding: this.encoding
|
||||
encoding: this.encoding.value
|
||||
})
|
||||
.then(() => {
|
||||
this.isPreviewOpen = true;
|
||||
|
||||
@@ -456,15 +456,15 @@
|
||||
}
|
||||
|
||||
.bottom-right {
|
||||
bottom: var(--spacer);
|
||||
right: var(--spacer);
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: calc(var(--spacer) / 2);
|
||||
//gap: calc(var(--spacer) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
121
ui/src/components/inputs/EditorButtons.vue
Normal file
121
ui/src/components/inputs/EditorButtons.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="to-action-button">
|
||||
<div v-if="isAllowedEdit || canDelete" class="mx-2">
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="isReadOnly">
|
||||
<DotsVertical title=""/>
|
||||
{{ $t("actions") }}
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="m-dropdown-menu">
|
||||
<el-dropdown-item
|
||||
v-if="!isCreating && canDelete"
|
||||
:icon="Delete"
|
||||
size="large"
|
||||
@click="forwardEvent('delete-flow', $event)"
|
||||
>
|
||||
{{ $t("delete") }}
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
v-if="!isCreating"
|
||||
:icon="ContentCopy"
|
||||
size="large"
|
||||
@click="forwardEvent('copy', $event)"
|
||||
>
|
||||
{{ $t("copy") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="Exclamation"
|
||||
size="large"
|
||||
@click="forwardEvent('open-new-error', null)"
|
||||
:disabled="!flowHaveTasks"
|
||||
>
|
||||
{{ $t("add global error handler") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="LightningBolt"
|
||||
size="large"
|
||||
@click="forwardEvent('open-new-trigger', null)"
|
||||
:disabled="!flowHaveTasks"
|
||||
>
|
||||
{{ $t("add trigger") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="FileEdit"
|
||||
size="large"
|
||||
@click="forwardEvent('open-edit-metadata', null)"
|
||||
>
|
||||
{{ $t("edit metadata") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div>
|
||||
<el-button
|
||||
:icon="ContentSave"
|
||||
@click="forwardEvent('save', $event)"
|
||||
v-if="isAllowedEdit"
|
||||
:type="flowError ? 'danger' : 'primary'"
|
||||
:disabled="!haveChange && !isCreating"
|
||||
class="edit-flow-save-button"
|
||||
>
|
||||
{{ $t("save") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import DotsVertical from "vue-material-design-icons/DotsVertical.vue";
|
||||
import Delete from "vue-material-design-icons/Delete.vue";
|
||||
import ContentCopy from "vue-material-design-icons/ContentCopy.vue";
|
||||
import Exclamation from "vue-material-design-icons/Exclamation.vue";
|
||||
import LightningBolt from "vue-material-design-icons/LightningBolt.vue";
|
||||
import FileEdit from "vue-material-design-icons/FileEdit.vue";
|
||||
import ContentSave from "vue-material-design-icons/ContentSave.vue";
|
||||
</script>
|
||||
<script>
|
||||
import {defineComponent} from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
isCreating: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
canDelete: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isAllowedEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
haveChange: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
flowHaveTasks: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
flowError: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
forwardEvent(type, event) {
|
||||
this.$emit(type, event);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -28,6 +28,7 @@
|
||||
import {editorViewTypes} from "../../utils/constants";
|
||||
import Utils from "@kestra-io/ui-libs/src/utils/Utils";
|
||||
import {apiUrl} from "override/utils/route";
|
||||
import EditorButtons from "./EditorButtons.vue";
|
||||
|
||||
const store = useStore();
|
||||
const router = getCurrentInstance().appContext.config.globalProperties.$router;
|
||||
@@ -675,75 +676,22 @@
|
||||
<ValidationError ref="validationDomElement" tooltip-placement="bottom-start" size="small" class="ms-2" :error="flowError" :warnings="flowWarnings" />
|
||||
</template>
|
||||
<template #buttons>
|
||||
<ul>
|
||||
<li v-if="isAllowedEdit || canDelete">
|
||||
<el-dropdown>
|
||||
<el-button type="default" :disabled="isReadOnly">
|
||||
<DotsVertical title="" />
|
||||
{{ $t("actions") }}
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="m-dropdown-menu">
|
||||
<el-dropdown-item
|
||||
v-if="!props.isCreating && canDelete"
|
||||
:icon="Delete"
|
||||
size="large"
|
||||
@click="deleteFlow"
|
||||
>
|
||||
{{ $t("delete") }}
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
v-if="!props.isCreating"
|
||||
:icon="ContentCopy"
|
||||
size="large"
|
||||
@click="() => router.push({name: 'flows/create', query: {copy: true}})"
|
||||
>
|
||||
{{ $t("copy") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="Exclamation"
|
||||
size="large"
|
||||
@click="isNewErrorOpen = true;"
|
||||
:disabled="!flowHaveTasks()"
|
||||
>
|
||||
{{ $t("add global error handler") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="LightningBolt"
|
||||
size="large"
|
||||
@click="isNewTriggerOpen = true;"
|
||||
:disabled="!flowHaveTasks()"
|
||||
>
|
||||
{{ $t("add trigger") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="isAllowedEdit"
|
||||
:icon="FileEdit"
|
||||
size="large"
|
||||
@click="isEditMetadataOpen = true;"
|
||||
>
|
||||
{{ $t("edit metadata") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</li>
|
||||
<li>
|
||||
<el-button
|
||||
:icon="ContentSave"
|
||||
@click="save"
|
||||
v-if="isAllowedEdit"
|
||||
:type="flowError ? 'danger' : 'primary'"
|
||||
:disabled="!haveChange && !isCreating"
|
||||
class="edit-flow-save-button"
|
||||
>
|
||||
{{ $t("save") }}
|
||||
</el-button>
|
||||
</li>
|
||||
</ul>
|
||||
<EditorButtons
|
||||
v-if="![editorViewTypes.TOPOLOGY, editorViewTypes.SOURCE_TOPOLOGY].includes(viewType)"
|
||||
:is-creating="props.isCreating"
|
||||
:is-read-only="props.isReadOnly"
|
||||
:can-delete="canDelete"
|
||||
:is-allowed-edit="isAllowedEdit"
|
||||
:have-change="haveChange"
|
||||
:flow-have-tasks="flowHaveTasks()"
|
||||
:flow-error="flowError"
|
||||
@delete-flow="deleteFlow"
|
||||
@save="save"
|
||||
@copy="() => router.push({name: 'flows/create', query: {copy: true}})"
|
||||
@open-new-error="isNewErrorOpen = true;"
|
||||
@open-new-trigger="isNewTriggerOpen = true;"
|
||||
@open-edit-metadata="isEditMetadataOpen = true;"
|
||||
/>
|
||||
</template>
|
||||
</editor>
|
||||
<div class="slider" @mousedown="dragEditor" v-if="combinedEditor" />
|
||||
@@ -859,6 +807,22 @@
|
||||
class="to-topology-button"
|
||||
@switch-view="switchViewType"
|
||||
/>
|
||||
<EditorButtons
|
||||
v-if="[editorViewTypes.TOPOLOGY, editorViewTypes.SOURCE_TOPOLOGY].includes(viewType)"
|
||||
:is-creating="props.isCreating"
|
||||
:is-read-only="props.isReadOnly"
|
||||
:can-delete="canDelete"
|
||||
:is-allowed-edit="isAllowedEdit"
|
||||
:have-change="haveChange"
|
||||
:flow-have-tasks="flowHaveTasks()"
|
||||
:flow-error="flowError"
|
||||
@delete-flow="deleteFlow"
|
||||
@save="save"
|
||||
@copy="() => router.push({name: 'flows/create', query: {copy: true}})"
|
||||
@open-new-error="isNewErrorOpen = true;"
|
||||
@open-new-trigger="isNewTriggerOpen = true;"
|
||||
@open-edit-metadata="isEditMetadataOpen = true;"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
@@ -879,6 +843,13 @@
|
||||
right: 45px;
|
||||
}
|
||||
|
||||
.to-action-button {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
right: 45px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.editor-combined {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
:allow-auto-expand-subflows="false"
|
||||
:target-execution-id="currentTaskRun.outputs.executionId"
|
||||
:class="$el.classList.contains('even') ? '' : 'even'"
|
||||
:show-progress-bar="showProgressBar"
|
||||
:show-logs="showLogs"
|
||||
/>
|
||||
</DynamicScrollerItem>
|
||||
</template>
|
||||
@@ -315,6 +317,16 @@
|
||||
this.logsWithIndexByAttemptUid[this.attemptUid(taskRun.id, this.selectedAttemptNumberByTaskRunId[taskRun.id])])) &&
|
||||
this.showLogs
|
||||
},
|
||||
followExecution(executionId) {
|
||||
this.$store
|
||||
.dispatch("execution/followExecution", {id: executionId})
|
||||
.then(sse => {
|
||||
this.executionSSE = sse;
|
||||
this.executionSSE.onmessage = async (event) => {
|
||||
this.followedExecution = JSON.parse(event.data);
|
||||
}
|
||||
});
|
||||
},
|
||||
followLogs(executionId) {
|
||||
this.$store
|
||||
.dispatch("execution/followLogs", {id: executionId})
|
||||
|
||||
@@ -413,11 +413,6 @@ form.ks-horizontal {
|
||||
|
||||
&.is-dark {
|
||||
color: var(--bs-gray-100);
|
||||
html:not(.dark) & {
|
||||
* {
|
||||
color: var(--bs-gray-100);
|
||||
}
|
||||
}
|
||||
|
||||
background: var(--bs-gray-900);
|
||||
border: 1px solid var(--bs-border-color);
|
||||
|
||||
@@ -1009,12 +1009,11 @@ public class ExecutionController {
|
||||
@Parameter(description = "The execution id") @PathVariable String executionId,
|
||||
@Parameter(description = "The internal storage uri") @QueryValue URI path,
|
||||
@Parameter(description = "The max row returns") @QueryValue @Nullable Integer maxRows,
|
||||
@Parameter(description = "The file encoding as Java charset name. Defaults to UTF-8", example = "ISO-8859-1") @QueryValue @Nullable String encoding
|
||||
@Parameter(description = "The file encoding as Java charset name. Defaults to UTF-8", example = "ISO-8859-1") @QueryValue(defaultValue = "UTF-8") String encoding
|
||||
) throws IOException {
|
||||
this.validateFile(executionId, path, "/api/v1/executions/{executionId}/file?path=" + path);
|
||||
|
||||
String extension = FilenameUtils.getExtension(path.toString());
|
||||
InputStream fileStream = storageInterface.get(tenantService.resolveTenant(), path);
|
||||
Optional<Charset> charset;
|
||||
|
||||
try {
|
||||
@@ -1023,13 +1022,15 @@ public class ExecutionController {
|
||||
throw new IllegalArgumentException("Unable to preview using encoding '" + encoding + "'");
|
||||
}
|
||||
|
||||
FileRender fileRender = FileRenderBuilder.of(
|
||||
extension,
|
||||
fileStream,
|
||||
charset,
|
||||
maxRows == null ? this.initialPreviewRows : (maxRows > this.maxPreviewRows ? this.maxPreviewRows : maxRows)
|
||||
);
|
||||
try (InputStream fileStream = storageInterface.get(tenantService.resolveTenant(), path)){
|
||||
FileRender fileRender = FileRenderBuilder.of(
|
||||
extension,
|
||||
fileStream,
|
||||
charset,
|
||||
maxRows == null ? this.initialPreviewRows : (maxRows > this.maxPreviewRows ? this.maxPreviewRows : maxRows)
|
||||
);
|
||||
|
||||
return HttpResponse.ok(fileRender);
|
||||
return HttpResponse.ok(fileRender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,9 @@ public class NamespaceFileController {
|
||||
) throws IOException, URISyntaxException {
|
||||
ensureWritableFile(path);
|
||||
|
||||
storageInterface.put(tenantService.resolveTenant(), toNamespacedStorageUri(namespace, path), new BufferedInputStream(fileContent.getInputStream()));
|
||||
try(BufferedInputStream inputStream = new BufferedInputStream(fileContent.getInputStream())) {
|
||||
storageInterface.put(tenantService.resolveTenant(), toNamespacedStorageUri(namespace, path), inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@ExecuteOn(TaskExecutors.IO)
|
||||
|
||||
Reference in New Issue
Block a user