Compare commits

...

15 Commits

Author SHA1 Message Date
YannC
1c8177e185 chore: upgrade to version 0.17.1 2024-06-05 22:38:53 +02:00
brian.mulier
3dd5d6bb71 fix(ui): prevent the need of loading all flows for Flow tab to be displayed in editor 2024-06-05 22:38:53 +02:00
YannC
16a641693a fix(ui): avoid 404 with autocomplete when flow does not exist 2024-06-05 22:21:07 +02:00
YannC
efdb075155 fix(core): Now accept an extension for the file input
close #3858
2024-06-05 22:21:02 +02:00
Miloš Paunović
a99d52a406 fix(ui): added safety checks for all tour related calls (#3938) 2024-06-05 22:20:53 +02:00
YannC
852edea36e fix(ui): dont count flow in tutorial namespace 2024-06-05 22:20:45 +02:00
brian.mulier
defa426259 fix(ui): null-safe guided tour access in TriggerFlow.vue 2024-06-05 22:20:38 +02:00
Miloš Paunović
3aadcfd683 fix(ui): flow default inputs are now properly populated (#3934) 2024-06-05 22:20:30 +02:00
YannC
0f5d59103a fix(core): remove @NotEmpty
close #3920
2024-06-05 22:20:16 +02:00
YannC
50b9120434 fix(core): UploadFiles now handle subfolders 2024-06-05 22:19:53 +02:00
Anna Geller
896c761502 feat: switch from contact-us to demo 2024-06-05 22:19:39 +02:00
Loïc Mathieu
381d1b381f chore: fix docker image build 2024-06-04 15:29:51 +02:00
Loïc Mathieu
72a428a439 core: add default 'true' to docker task 2024-06-04 14:45:57 +02:00
Loïc Mathieu
7447e61dbc chore: fix docker workflow variable computation 2024-06-04 14:45:52 +02:00
Loïc Mathieu
45ffc3cc22 fix: Maven description 2024-06-04 11:11:48 +02:00
20 changed files with 84 additions and 57 deletions

View File

@@ -7,14 +7,7 @@ on:
description: 'Retag latest Docker images'
required: true
type: string
options:
- "true"
- "false"
skip-test:
description: 'Skip test'
required: false
type: string
default: "false"
default: "true"
options:
- "true"
- "false"
@@ -125,6 +118,16 @@ jobs:
packages: python3 python3-venv python-is-python3 python3-pip nodejs npm curl zip unzip
python-libs: kestra
steps:
- uses: actions/checkout@v4
# Vars
- name: Set image name
id: vars
run: |
TAG=${GITHUB_REF#refs/*/}
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "plugins=${{ matrix.image.plugins }}" >> $GITHUB_OUTPUT
# Download release
- name: Download release
uses: robinraju/release-downloader@v1.10
@@ -137,14 +140,6 @@ jobs:
run: |
cp build/executable/* docker/app/kestra && chmod +x docker/app/kestra
# Vars
- name: Set image name
id: vars
run: |
TAG=${GITHUB_REF#refs/*/}
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "plugins=${{ matrix.image.plugins }}" >> $GITHUB_OUTPUT
# Docker setup
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -179,7 +174,7 @@ jobs:
- name: Retag to latest
if: github.event.inputs.retag-latest == 'true'
run: |
regctl image copy ${{ format('kestra/kestra:{0}{1}', steps.vars.outputs.tag, matrix.image.name) }} ${{ format('kestra/kestra:latest{1}', matrix.image.name) }}
regctl image copy ${{ format('kestra/kestra:{0}{1}', steps.vars.outputs.tag, matrix.image.name) }} ${{ format('kestra/kestra:latest{0}', matrix.image.name) }}
end:
runs-on: ubuntu-latest

View File

@@ -454,7 +454,7 @@ subprojects {
}
maven.pom {
description 'The modern, scalable orchestrator & scheduler open source platform'
description = 'The modern, scalable orchestrator & scheduler open source platform'
developers {
developer {

View File

@@ -1,18 +1,21 @@
package io.kestra.core.models.flows.input;
import io.kestra.core.models.flows.Input;
import jakarta.validation.ConstraintViolationException;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.net.URI;
import jakarta.validation.ConstraintViolationException;
@SuperBuilder
@Getter
@NoArgsConstructor
public class FileInput extends Input<URI> {
@Builder.Default
public String extension = ".upl";
@Override
public void validate(URI input) throws ConstraintViolationException {
// no validation yet

View File

@@ -8,6 +8,7 @@ import io.kestra.core.models.flows.Flow;
import io.kestra.core.models.flows.Input;
import io.kestra.core.models.flows.Type;
import io.kestra.core.models.flows.input.ArrayInput;
import io.kestra.core.models.flows.input.FileInput;
import io.kestra.core.models.tasks.common.EncryptedString;
import io.kestra.core.serializers.JacksonMapper;
import io.kestra.core.storages.StorageInterface;
@@ -85,7 +86,9 @@ public class FlowInputOutput {
.subscribeOn(Schedulers.boundedElastic())
.map(throwFunction(input -> {
if (input instanceof CompletedFileUpload fileUpload) {
File tempFile = File.createTempFile(fileUpload.getFilename() + "_", ".upl");
String fileExtension = inputs.stream().filter(flowInput -> flowInput instanceof FileInput && flowInput.getId().equals(fileUpload.getFilename())).map(flowInput -> ((FileInput) flowInput).getExtension()).findFirst().orElse(".upl");
fileExtension = fileExtension.startsWith(".") ? fileExtension : "." + fileExtension;
File tempFile = File.createTempFile(fileUpload.getFilename() + "_", fileExtension);
try (var inputStream = fileUpload.getInputStream();
var outputStream = new FileOutputStream(tempFile)) {
long transferredBytes = inputStream.transferTo(outputStream);

View File

@@ -80,7 +80,6 @@ public class DeleteFiles extends Task implements RunnableTask<DeleteFiles.Output
private String namespace;
@NotNull
@NotEmpty
@Schema(
title = "A file or a list of files from the given namespace.",
description = "String or a list of strings; each string can either be a regex glob pattern or a file path URI.",

View File

@@ -11,7 +11,6 @@ import io.kestra.core.runners.RunContext;
import io.kestra.core.services.FlowService;
import io.kestra.core.utils.Rethrow;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -84,7 +83,6 @@ public class DownloadFiles extends Task implements RunnableTask<DownloadFiles.Ou
private String namespace;
@NotNull
@NotEmpty
@Schema(
title = "A file or a list of files from the given namespace.",
description = "String or a list of strings; each string can either be a regex glob pattern or a file path URI.",
@@ -93,11 +91,19 @@ public class DownloadFiles extends Task implements RunnableTask<DownloadFiles.Ou
@PluginProperty(dynamic = true)
private Object files;
@Schema(
title = "The folder where the downloaded files will be stored"
)
@PluginProperty(dynamic = true)
@Builder.Default
private String destination = "";
@Override
public Output run(RunContext runContext) throws Exception {
Logger logger = runContext.logger();
String renderedNamespace = runContext.render(namespace);
String renderedDestination = runContext.render(destination);
// Check if namespace is allowed
RunContext.FlowInfo flowInfo = runContext.flowInfo();
FlowService flowService = runContext.getApplicationContext().getBean(FlowService.class);
@@ -120,7 +126,7 @@ public class DownloadFiles extends Task implements RunnableTask<DownloadFiles.Ou
namespaceFilesService.recursiveList(flowInfo.tenantId(), renderedNamespace, null).forEach(Rethrow.throwConsumer(uri -> {
if (patterns.stream().anyMatch(p -> p.matches(Path.of(uri.getPath())))) {
try (InputStream inputStream = namespaceFilesService.content(flowInfo.tenantId(), renderedNamespace, uri)) {
downloaded.put(uri.getPath(), runContext.storage().putFile(inputStream, uri.getPath()));
downloaded.put(uri.getPath(), runContext.storage().putFile(inputStream, destination + uri.getPath()));
logger.debug(String.format("Downloaded %s", uri));
}
}

View File

@@ -138,10 +138,10 @@ public class UploadFiles extends Task implements RunnableTask<UploadFiles.Output
});
// check for file in current tempDir that match regexs
List<PathMatcher> patterns = regexs.stream().map(reg -> FileSystems.getDefault().getPathMatcher("glob:" + reg)).toList();
for (File file : Objects.requireNonNull(runContext.tempDir().toFile().listFiles())) {
List<PathMatcher> patterns = regexs.stream().map(reg -> FileSystems.getDefault().getPathMatcher("glob:" + runContext.tempDir().toString() + checkLeadingSlash(reg))).toList();
for (File file : Objects.requireNonNull(listFilesRecursively(runContext.tempDir().toFile()))) {
if (patterns.stream().anyMatch(p -> p.matches(Path.of(file.toURI().getPath())))) {
String newFilePath = buildPath(renderedDestination, file.getName());
String newFilePath = buildPath(renderedDestination, file.getPath().replace(runContext.tempDir().toString(), ""));
storeNewFile(logger, runContext, storageInterface, flowInfo.tenantId(), newFilePath, new FileInputStream(file));
}
}
@@ -199,6 +199,24 @@ public class UploadFiles extends Task implements RunnableTask<UploadFiles.Output
}
}
private List<File> listFilesRecursively(File directory) throws IOException {
List<File> files = new ArrayList<>();
if (directory == null || !directory.isDirectory()) {
return files; // Handle invalid directory or not a directory
}
for (File file : directory.listFiles()) {
if (file.isFile()) {
files.add(file);
} else {
// Recursively call for subdirectories
files.addAll(listFilesRecursively(file));
}
}
return files;
}
@Builder
@Getter
public static class Output implements io.kestra.core.models.tasks.Output {

View File

@@ -1,4 +1,4 @@
version=0.17.0
version=0.17.1
jacksonVersion=2.16.2
micronautVersion=4.4.3

View File

@@ -41,7 +41,7 @@
if (this.$route.query.reset) {
localStorage.setItem("tourDoneOrSkip", undefined);
this.$store.commit("core/setGuidedProperties", {tourStarted: false});
this.$tours["guidedTour"].start();
this.$tours["guidedTour"]?.start();
}
this.setupFlow()
},

View File

@@ -67,7 +67,7 @@
},
methods: {
stopTour() {
this.$tours["guidedTour"].stop();
this.$tours["guidedTour"]?.stop();
this.$store.commit("core/setGuidedProperties", {tourStarted: false});
},
},
@@ -79,7 +79,7 @@
if (!this.guidedProperties.tourStarted
&& localStorage.getItem("tourDoneOrSkip") !== "true"
&& this.total === 0) {
this.$tours["guidedTour"].start();
this.$tours["guidedTour"]?.start();
}
}, 200)
window.addEventListener("popstate", () => {

View File

@@ -55,7 +55,7 @@
handler: function (newValue) {
if (newValue?.manuallyContinue) {
setTimeout(() => {
this.$tours["guidedTour"].nextStep();
this.$tours["guidedTour"]?.nextStep();
this.$store.commit("core/setGuidedProperties", {manuallyContinue: false});
}, 500);
}

View File

@@ -91,12 +91,12 @@
},
methods: {
onClick() {
if (this.$tours["guidedTour"].isRunning.value) {
this.$tours["guidedTour"].nextStep();
if (this.$tours["guidedTour"]?.isRunning?.value) {
this.$tours["guidedTour"]?.nextStep();
this.$store.dispatch("api/events", {
type: "ONBOARDING",
onboarding: {
step: this.$tours["guidedTour"].currentStep._value,
step: this.$tours["guidedTour"]?.currentStep?._value,
action: "next",
template: this.guidedProperties.template
},
@@ -131,7 +131,7 @@
},
beforeClose(done){
if(this.guidedProperties.tourStarted) return;
this.reset();
done()
}

View File

@@ -385,7 +385,7 @@
},
computed: {
...mapState({
flows: (state) => state.flow.flows,
flow: (state) => state.flow.flow,
explorerVisible: (state) => state.editor.explorerVisible,
}),
folders() {
@@ -765,17 +765,17 @@
(function pushItemToFolder(basePath = "", array) {
for (const item of array) {
const folderPath = `${basePath}${item.fileName}`;
if (folderPath === SELF.dialog.folder && Array.isArray(item.children)) {
item.children = SELF.sorted([...item.children, NEW]);
return true; // Return true if the folder is found and item is pushed
}
if (Array.isArray(item.children) && pushItemToFolder(`${folderPath}/`, item.children)) {
return true; // Return true if the folder is found and item is pushed in recursive call
}
}
return false;
})(undefined, this.items);
}
@@ -883,9 +883,9 @@
},
},
watch: {
flows: {
flow: {
handler(flow) {
if (flow && flow.length) {
if (flow) {
this.changeOpenedTabs({
action: "open",
name: "Flow",
@@ -948,21 +948,21 @@
.empty {
position: relative;
top: 100px;
text-align: center;
text-align: center;
color: white;
html.light & {
color: $tertiary;
}
& img {
margin-bottom: 2rem;
margin-bottom: 2rem;
}
& h3 {
font-size: var(--font-size-lg);
font-weight: 500;
margin-bottom: .5rem;
font-weight: 500;
margin-bottom: .5rem;
}
& p {

View File

@@ -430,7 +430,8 @@
id: subflowTask.flowId,
revision: subflowTask.revision,
source: false,
store: false
store: false,
deleted: true
}
)).inputs?.map(input => input.id) ?? [];
} catch (e) {

View File

@@ -69,7 +69,7 @@
<Slack class="align-middle" /> {{ $t("join community") }}
</a>
<a
href="https://kestra.io/contact-us?utm_source=app&utm_content=top-nav-bar"
href="https://kestra.io/demo?utm_source=app&utm_content=top-nav-bar"
target="_blank"
class="d-flex gap-2 el-dropdown-menu__item"
>
@@ -147,7 +147,7 @@
localStorage.setItem("tourDoneOrSkip", undefined);
this.$store.commit("core/setGuidedProperties", {tourStarted: false});
this.$tours["guidedTour"].start();
this.$tours["guidedTour"]?.start();
}
}
};

View File

@@ -183,17 +183,17 @@ export default {
}
},
save() {
if (this.$tours["guidedTour"].isRunning.value && !this.guidedProperties.saveFlow) {
if (this.$tours["guidedTour"]?.isRunning?.value && !this.guidedProperties.saveFlow) {
this.$store.dispatch("api/events", {
type: "ONBOARDING",
onboarding: {
step: this.$tours["guidedTour"].currentStep._value,
step: this.$tours["guidedTour"]?.currentStep?._value,
action: "next",
template: this.guidedProperties.template
},
page: pageFromRoute(this.$router.currentRoute.value)
});
this.$tours["guidedTour"].nextStep();
this.$tours["guidedTour"]?.nextStep();
return;
}

View File

@@ -37,7 +37,7 @@ export default {
}).then(response => {
commit("setFlows", response.data.results)
commit("setTotal", response.data.total)
commit("setOverallTotal", response.data.total)
commit("setOverallTotal", response.data.results.filter(f => f.namespace !== "tutorial").length)
return response.data;
})

View File

@@ -546,7 +546,7 @@
"environment color setting": "Environment color",
"slack support": "Ask any question via Slack",
"join community": "Join the Community",
"reach us": "Reach out to us",
"reach us": "Talk to us",
"new version": "New version {version} available!",
"error detected": "Error(s) detected",
"warning detected": "Warning(s) detected",

View File

@@ -14,6 +14,8 @@ export default class Inputs {
res = JSON.stringify(res).toString()
} else if (type === "BOOLEAN" && type === undefined){
res = "undefined";
} else if (type === "STRING" && Array.isArray(res)){
res = res.toString();
}
return res;
}

View File

@@ -84,7 +84,7 @@ export const executeTask = (submitor, flow, values, options) => {
}
}
if(options.nextStep) submitor.$tours["guidedTour"].nextStep();
if(options.nextStep) submitor.$tours["guidedTour"]?.nextStep();
return response.data;
})