mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-30 03:00:23 -05:00
Compare commits
40 Commits
spike/gene
...
fix/issue-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2409aa538 | ||
|
|
b8a363d315 | ||
|
|
bd582a5a45 | ||
|
|
a254de0d0d | ||
|
|
1b74842485 | ||
|
|
b178483dd3 | ||
|
|
7552bdb3a1 | ||
|
|
f1bdcd6662 | ||
|
|
dc37112c1d | ||
|
|
4733f7eb8a | ||
|
|
135a9fcdaa | ||
|
|
39b83b343c | ||
|
|
b169f4812a | ||
|
|
b3b3e40e53 | ||
|
|
506d7ae697 | ||
|
|
fc3a0fbc33 | ||
|
|
0b55f5531b | ||
|
|
16b6a82420 | ||
|
|
2fcbf72b34 | ||
|
|
801e347b11 | ||
|
|
83adc8ce57 | ||
|
|
cb31e48f4f | ||
|
|
a3f96a2741 | ||
|
|
5ca6fa8d77 | ||
|
|
a3a206f3c4 | ||
|
|
31f1e505e3 | ||
|
|
75e0c1d11f | ||
|
|
4cf883877d | ||
|
|
87b1e8fb01 | ||
|
|
b5c6101090 | ||
|
|
e4323728d6 | ||
|
|
5495971ecf | ||
|
|
dec1ee4272 | ||
|
|
69cc6b2715 | ||
|
|
431b4ccdb9 | ||
|
|
e9207a6f53 | ||
|
|
419c1041d5 | ||
|
|
02cd5efb05 | ||
|
|
2d549940c4 | ||
|
|
97e138fbae |
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -63,9 +63,9 @@ You can also build it from a terminal using `./gradlew build`, the Gradle wrappe
|
||||
- Configure the following environment variables:
|
||||
- `MICRONAUT_ENVIRONMENTS`: can be set to any string and will load a custom configuration file in `cli/src/main/resources/application-{env}.yml`.
|
||||
- `KESTRA_PLUGINS_PATH`: is the path where you will save plugins as Jar and will be load on startup.
|
||||
- See the screenshot below for an example: 
|
||||
- See the screenshot below for an example: 
|
||||
- If you encounter **JavaScript memory heap out** error during startup, configure `NODE_OPTIONS` environment variable with some large value.
|
||||
- Example `NODE_OPTIONS: --max-old-space-size=4096` or `NODE_OPTIONS: --max-old-space-size=8192` 
|
||||
- Example `NODE_OPTIONS: --max-old-space-size=4096` or `NODE_OPTIONS: --max-old-space-size=8192` 
|
||||
- The server starts by default on port 8080 and is reachable on `http://localhost:8080`
|
||||
|
||||
If you want to launch all tests, you need Python and some packages installed on your machine, on Ubuntu you can install them with:
|
||||
|
||||
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
12
build.gradle
12
build.gradle
@@ -21,7 +21,7 @@ plugins {
|
||||
|
||||
// test
|
||||
id "com.adarshr.test-logger" version "4.0.0"
|
||||
id "org.sonarqube" version "7.2.1.6560"
|
||||
id "org.sonarqube" version "7.2.2.6593"
|
||||
id 'jacoco-report-aggregation'
|
||||
|
||||
// helper
|
||||
@@ -185,6 +185,7 @@ subprojects {subProj ->
|
||||
canBeResolved = true
|
||||
canBeConsumed = true
|
||||
}
|
||||
mockitoAgent
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -205,6 +206,9 @@ subprojects {subProj ->
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit-pioneer:junit-pioneer"
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
mockitoAgent("org.mockito:mockito-core:5.21.0") {
|
||||
transitive = false // just the core
|
||||
}
|
||||
|
||||
// hamcrest
|
||||
testImplementation 'org.hamcrest:hamcrest'
|
||||
@@ -222,6 +226,7 @@ subprojects {subProj ->
|
||||
|
||||
def commonTestConfig = { Test t ->
|
||||
t.ignoreFailures = true
|
||||
t.finalizedBy jacocoTestReport
|
||||
|
||||
// set Xmx for test workers
|
||||
t.maxHeapSize = '4g'
|
||||
@@ -328,7 +333,10 @@ subprojects {subProj ->
|
||||
junitXml.outputLocation = layout.buildDirectory.dir("test-results/test")
|
||||
}
|
||||
commonTestConfig(t)
|
||||
jvmArgs = ["-javaagent:${configurations.agent.singleFile}"]
|
||||
jvmArgs = [
|
||||
"-javaagent:${configurations.agent.singleFile}",
|
||||
"-javaagent:${configurations.mockitoAgent.singleFile}"
|
||||
]
|
||||
}
|
||||
|
||||
tasks.named('check') {
|
||||
|
||||
@@ -81,7 +81,7 @@ public class MetadataMigrationService {
|
||||
}));
|
||||
}
|
||||
|
||||
public void nsFilesMigration() throws IOException {
|
||||
public void nsFilesMigration(boolean verbose) throws IOException {
|
||||
this.namespacesPerTenant().entrySet().stream()
|
||||
.flatMap(namespacesForTenant -> namespacesForTenant.getValue().stream().map(namespace -> Map.entry(namespacesForTenant.getKey(), namespace)))
|
||||
.flatMap(throwFunction(namespaceForTenant -> {
|
||||
@@ -92,6 +92,9 @@ public class MetadataMigrationService {
|
||||
.forEach(throwConsumer(nsFileMetadata -> {
|
||||
if (namespaceFileMetadataRepository.findByPath(nsFileMetadata.getTenantId(), nsFileMetadata.getNamespace(), nsFileMetadata.getPath()).isEmpty()) {
|
||||
namespaceFileMetadataRepository.save(nsFileMetadata);
|
||||
if (verbose) {
|
||||
System.out.println("Migrated namespace file metadata: " + nsFileMetadata.getNamespace() + " - " + nsFileMetadata.getPath());
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@ public class NsFilesMetadataMigrationCommand extends AbstractCommand {
|
||||
@Inject
|
||||
private Provider<MetadataMigrationService> metadataMigrationServiceProvider;
|
||||
|
||||
@CommandLine.Option(names = {"-lm", "--log-migrations"}, description = "Log all files that are migrated", defaultValue = "false")
|
||||
public boolean logMigrations = false;
|
||||
|
||||
@Override
|
||||
public Integer call() throws Exception {
|
||||
super.call();
|
||||
try {
|
||||
metadataMigrationServiceProvider.get().nsFilesMigration();
|
||||
metadataMigrationServiceProvider.get().nsFilesMigration(logMigrations);
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Namespace Files Metadata migration failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.micronaut.configuration.picocli.PicocliRunner;
|
||||
import io.micronaut.context.ApplicationContext;
|
||||
import io.micronaut.context.env.Environment;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
@@ -15,7 +14,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -25,7 +23,8 @@ class PluginDocCommandTest {
|
||||
|
||||
@Test
|
||||
void run() throws IOException, URISyntaxException {
|
||||
Path pluginsPath = Files.createTempDirectory(PluginListCommandTest.class.getSimpleName());
|
||||
var testDirectoryName = PluginListCommandTest.class.getSimpleName();
|
||||
Path pluginsPath = Files.createTempDirectory(testDirectoryName + "_pluginsPath_");
|
||||
pluginsPath.toFile().deleteOnExit();
|
||||
|
||||
FileUtils.copyFile(
|
||||
@@ -34,7 +33,7 @@ class PluginDocCommandTest {
|
||||
new File(URI.create("file://" + pluginsPath.toAbsolutePath() + "/" + PLUGIN_TEMPLATE_TEST))
|
||||
);
|
||||
|
||||
Path docPath = Files.createTempDirectory(PluginInstallCommandTest.class.getSimpleName());
|
||||
Path docPath = Files.createTempDirectory(testDirectoryName + "_docPath_");
|
||||
docPath.toFile().deleteOnExit();
|
||||
|
||||
try (ApplicationContext ctx = ApplicationContext.run(Environment.CLI, Environment.TEST)) {
|
||||
@@ -43,9 +42,9 @@ class PluginDocCommandTest {
|
||||
|
||||
List<Path> files = Files.list(docPath).toList();
|
||||
|
||||
assertThat(files.size()).isEqualTo(1);
|
||||
assertThat(files.getFirst().getFileName().toString()).isEqualTo("plugin-template-test");
|
||||
var directory = files.getFirst().toFile();
|
||||
assertThat(files.stream().map(path -> path.getFileName().toString())).contains("plugin-template-test");
|
||||
// don't know why, but sometimes there is an addition "plugin-notifications" directory present
|
||||
var directory = files.stream().filter(path -> "plugin-template-test".equals(path.getFileName().toString())).findFirst().get().toFile();
|
||||
assertThat(directory.isDirectory()).isTrue();
|
||||
assertThat(directory.listFiles().length).isEqualTo(3);
|
||||
|
||||
|
||||
@@ -185,15 +185,11 @@ public class TaskRun implements TenantInterface {
|
||||
}
|
||||
|
||||
public TaskRunAttempt lastAttempt() {
|
||||
if (this.attempts == null) {
|
||||
if (this.attempts == null || this.attempts.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this
|
||||
.attempts
|
||||
.stream()
|
||||
.reduce((a, b) -> b)
|
||||
.orElse(null);
|
||||
return this.attempts.getLast();
|
||||
}
|
||||
|
||||
public TaskRun onRunningResend() {
|
||||
@@ -264,8 +260,7 @@ public class TaskRun implements TenantInterface {
|
||||
* @return The next retry date, null if maxAttempt || maxDuration is reached
|
||||
*/
|
||||
public Instant nextRetryDate(AbstractRetry retry, Execution execution) {
|
||||
if (retry.getMaxAttempts() != null && execution.getMetadata().getAttemptNumber() >= retry.getMaxAttempts()) {
|
||||
|
||||
if (this.attempts == null || this.attempts.isEmpty() || retry.getMaxAttempts() != null && execution.getMetadata().getAttemptNumber() >= retry.getMaxAttempts()) {
|
||||
return null;
|
||||
}
|
||||
Instant base = this.lastAttempt().getState().maxDate();
|
||||
|
||||
@@ -148,6 +148,11 @@ public class State {
|
||||
return this.current.isTerminated();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean canBeRestarted() {
|
||||
return this.current.isTerminated() || this.current.isPaused();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isTerminatedNoFail() {
|
||||
return this.current.isTerminatedNoFail();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.kestra.core.runners.pebble.functions;
|
||||
|
||||
import com.cronutils.utils.VisibleForTesting;
|
||||
import io.kestra.core.runners.LocalPath;
|
||||
import io.kestra.core.runners.LocalPathFactory;
|
||||
import io.kestra.core.services.NamespaceService;
|
||||
@@ -155,31 +156,11 @@ abstract class AbstractFileFunction implements Function {
|
||||
}
|
||||
|
||||
private String checkIfFileFromAllowedNamespaceAndReturnIt(URI path, String tenantId, String fromNamespace) {
|
||||
// Extract namespace from the path, it should be of the form: kestra:///({tenantId}/){namespace}/{flowId}/executions/{executionId}/tasks/{taskId}/{taskRunId}/{fileName}'
|
||||
// To extract the namespace, we must do it step by step as tenantId, namespace and taskId can contain the words 'executions' and 'tasks'
|
||||
String namespace = path.toString().substring(KESTRA_SCHEME.length());
|
||||
if (!EXECUTION_FILE.matcher(namespace).matches()) {
|
||||
throw new IllegalArgumentException("Unable to read the file '" + path + "' as it is not an execution file");
|
||||
}
|
||||
|
||||
// 1. remove the tenantId if existing
|
||||
if (tenantId != null) {
|
||||
namespace = namespace.substring(tenantId.length() + 1);
|
||||
}
|
||||
// 2. remove everything after tasks
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf("/tasks/"));
|
||||
// 3. remove everything after executions
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf("/executions/"));
|
||||
// 4. remove the flowId
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf('/'));
|
||||
// 5. replace '/' with '.'
|
||||
namespace = namespace.replace("/", ".");
|
||||
|
||||
String namespace = extractNamespace(path);
|
||||
namespaceService.checkAllowedNamespace(tenantId, namespace, tenantId, fromNamespace);
|
||||
|
||||
return namespace;
|
||||
}
|
||||
|
||||
private String checkEnabledLocalFileAndReturnNamespace(Map<String, Object> args, Map<String, String> flow) {
|
||||
if (!enableFileProtocol) {
|
||||
throw new SecurityException("The file:// protocol has been disabled inside the Kestra configuration.");
|
||||
@@ -200,4 +181,24 @@ abstract class AbstractFileFunction implements Function {
|
||||
}
|
||||
return Optional.ofNullable(customNs).orElse(flow.get(NAMESPACE));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String extractNamespace( URI path){
|
||||
// Extract namespace from the path, it should be of the form: kestra:///{namespace}/{flowId}/executions/{executionId}/tasks/{taskId}/{taskRunId}/{fileName}'
|
||||
// To extract the namespace, we must do it step by step as namespace and taskId can contain the words 'executions' and 'tasks'
|
||||
String namespace = path.toString().substring(KESTRA_SCHEME.length());
|
||||
if (!EXECUTION_FILE.matcher(namespace).matches()) {
|
||||
throw new IllegalArgumentException("Unable to read the file '" + path + "' as it is not an execution file");
|
||||
}
|
||||
// 1. remove everything after tasks
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf("/tasks/"));
|
||||
// 2. remove everything after executions
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf("/executions/"));
|
||||
// 3. remove the flowId
|
||||
namespace = namespace.substring(0, namespace.lastIndexOf('/'));
|
||||
// 4. replace '/' with '.'
|
||||
namespace = namespace.replace("/", ".");
|
||||
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public class ExecutionService {
|
||||
}
|
||||
|
||||
public Execution restart(final Execution execution, @Nullable Integer revision) throws Exception {
|
||||
if (!(execution.getState().isTerminated() || execution.getState().isPaused())) {
|
||||
if (!execution.getState().canBeRestarted()) {
|
||||
throw new IllegalStateException("Execution must be terminated to be restarted, " +
|
||||
"current state is '" + execution.getState().getCurrent() + "' !"
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
@@ -62,14 +63,24 @@ public class SetVariables extends Task implements ExecutionUpdatableTask {
|
||||
public Execution update(Execution execution, RunContext runContext) throws Exception {
|
||||
Map<String, Object> renderedVars = runContext.render(this.variables).asMap(String.class, Object.class);
|
||||
boolean renderedOverwrite = runContext.render(overwrite).as(Boolean.class).orElseThrow();
|
||||
|
||||
Map<String, Object> currentVariables =
|
||||
execution.getVariables() == null ? Collections.emptyMap() : execution.getVariables();
|
||||
|
||||
if (!renderedOverwrite) {
|
||||
// check that none of the new variables already exist
|
||||
List<String> duplicated = renderedVars.keySet().stream().filter(key -> execution.getVariables().containsKey(key)).toList();
|
||||
List<String> duplicated = renderedVars.keySet().stream()
|
||||
.filter(currentVariables::containsKey)
|
||||
.toList();
|
||||
|
||||
if (!duplicated.isEmpty()) {
|
||||
throw new IllegalArgumentException("`overwrite` is set to false and the following variables already exist: " + String.join(",", duplicated));
|
||||
throw new IllegalArgumentException(
|
||||
"`overwrite` is set to false and the following variables already exist: " +
|
||||
String.join(",", duplicated)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return execution.withVariables(MapUtils.deepMerge(execution.getVariables(), renderedVars));
|
||||
return execution.withVariables(MapUtils.deepMerge(currentVariables, renderedVars));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ public class Webhook extends AbstractTrigger implements TriggerOutput<Webhook.Ou
|
||||
.flowId(flow.getId())
|
||||
.flowRevision(flow.getRevision())
|
||||
.inputs(inputs)
|
||||
.variables(flow.getVariables())
|
||||
.state(new State())
|
||||
.trigger(ExecutionTrigger.of(
|
||||
this,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.kestra.core.runners.pebble.functions;
|
||||
|
||||
import io.kestra.core.junit.annotations.KestraTest;
|
||||
import jakarta.inject.Inject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.net.URI;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@KestraTest
|
||||
public class AbstractFileFunctionTest {
|
||||
|
||||
@Inject
|
||||
ReadFileFunction readFileFunction;
|
||||
|
||||
@Test
|
||||
void namespaceFromURI(){
|
||||
String namespace1 = readFileFunction.extractNamespace(URI.create("kestra:///demo/simple-write-oss/executions/4Tnd2zrWGoHGrufwyt738j/tasks/write/2FOeylkRr5tktwIQqFh56w/18316959863401460785.txt"));
|
||||
assertThat(namespace1).isEqualTo("demo");
|
||||
|
||||
String namespace2 = readFileFunction.extractNamespace(URI.create("kestra:///io/kestra/tests/simple-write-oss/executions/4Tnd2zrWGoHGrufwyt738j/tasks/write/2FOeylkRr5tktwIQqFh56w/18316959863401460785.txt"));
|
||||
assertThat(namespace2).isEqualTo("io.kestra.tests");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () ->readFileFunction.extractNamespace(URI.create("kestra:///simple-write-oss/executions/4Tnd2zrWGoHGrufwyt738j/tasks/write/2FOeylkRr5tktwIQqFh56w/18316959863401460785.txt")));
|
||||
assertThrows(IllegalArgumentException.class, () ->readFileFunction.extractNamespace(URI.create("kestra:///executions/4Tnd2zrWGoHGrufwyt738j/tasks/write/2FOeylkRr5tktwIQqFh56w/18316959863401460785.txt")));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ tasks:
|
||||
message: "{{ task.id }}"
|
||||
- id: pause
|
||||
type: io.kestra.plugin.core.flow.Pause
|
||||
delay: PT1S
|
||||
pauseDuration: PT1S
|
||||
tasks:
|
||||
- id: c
|
||||
type: io.kestra.plugin.core.log.Log
|
||||
|
||||
@@ -297,10 +297,23 @@ public class JdbcExecutor implements ExecutorInterface {
|
||||
this.receiveCancellations.addFirst(((JdbcQueue<Execution>) this.executionQueue).receiveBatch(
|
||||
Executor.class,
|
||||
executions -> {
|
||||
List<CompletableFuture<Void>> futures = executions.stream()
|
||||
.map(execution -> CompletableFuture.runAsync(() -> executionQueue(execution), executionExecutorService))
|
||||
// process execution message grouped by executionId to avoid concurrency as the execution level as it would
|
||||
List<CompletableFuture<Void>> perExecutionFutures = executions.stream()
|
||||
.filter(Either::isLeft)
|
||||
.collect(Collectors.groupingBy(either -> either.getLeft().getId()))
|
||||
.values()
|
||||
.stream()
|
||||
.map(eithers -> CompletableFuture.runAsync(() -> {
|
||||
eithers.forEach(this::executionQueue);
|
||||
}, executionExecutorService))
|
||||
.toList();
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
|
||||
// directly process deserialization issues as most of the time there will be none
|
||||
executions.stream()
|
||||
.filter(Either::isRight)
|
||||
.forEach(either -> executionQueue(either));
|
||||
|
||||
CompletableFuture.allOf(perExecutionFutures.toArray(CompletableFuture[]::new)).join();
|
||||
}
|
||||
));
|
||||
this.receiveCancellations.addFirst(((JdbcQueue<WorkerTaskResult>) this.workerTaskResultQueue).receiveBatch(
|
||||
|
||||
@@ -35,7 +35,7 @@ dependencies {
|
||||
// we define cloud bom here for GCP, Azure and AWS so they are aligned for all plugins that use them (secret, storage, oss and ee plugins)
|
||||
api platform('com.google.cloud:libraries-bom:26.73.0')
|
||||
api platform("com.azure:azure-sdk-bom:1.3.3")
|
||||
api platform('software.amazon.awssdk:bom:2.40.10')
|
||||
api platform('software.amazon.awssdk:bom:2.40.15')
|
||||
api platform("dev.langchain4j:langchain4j-bom:$langchain4jVersion")
|
||||
api platform("dev.langchain4j:langchain4j-community-bom:$langchain4jCommunityVersion")
|
||||
|
||||
@@ -98,7 +98,7 @@ dependencies {
|
||||
api group: 'org.apache.maven.resolver', name: 'maven-resolver-connector-basic', version: mavenResolverVersion
|
||||
api group: 'org.apache.maven.resolver', name: 'maven-resolver-transport-file', version: mavenResolverVersion
|
||||
api group: 'org.apache.maven.resolver', name: 'maven-resolver-transport-apache', version: mavenResolverVersion
|
||||
api 'com.github.oshi:oshi-core:6.9.1'
|
||||
api 'com.github.oshi:oshi-core:6.9.2'
|
||||
api 'io.pebbletemplates:pebble:4.1.0'
|
||||
api group: 'co.elastic.logging', name: 'logback-ecs-encoder', version: '1.7.0'
|
||||
api group: 'de.focus-shift', name: 'jollyday-core', version: jollydayVersion
|
||||
|
||||
@@ -8,25 +8,31 @@ import io.micronaut.test.extensions.junit5.MicronautJunit5Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.platform.commons.support.AnnotationSupport;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class KestraTestExtension extends MicronautJunit5Extension {
|
||||
@Override
|
||||
protected MicronautTestValue buildMicronautTestValue(Class<?> testClass) {
|
||||
testProperties.put("kestra.jdbc.executor.thread-count", Runtime.getRuntime().availableProcessors() * 4);
|
||||
return AnnotationSupport
|
||||
.findAnnotation(testClass, KestraTest.class)
|
||||
.map(kestraTestAnnotation -> new MicronautTestValue(
|
||||
kestraTestAnnotation.application(),
|
||||
kestraTestAnnotation.environments(),
|
||||
kestraTestAnnotation.packages(),
|
||||
kestraTestAnnotation.propertySources(),
|
||||
kestraTestAnnotation.rollback(),
|
||||
kestraTestAnnotation.transactional(),
|
||||
kestraTestAnnotation.rebuildContext(),
|
||||
kestraTestAnnotation.contextBuilder(),
|
||||
kestraTestAnnotation.transactionMode(),
|
||||
kestraTestAnnotation.startApplication(),
|
||||
kestraTestAnnotation.resolveParameters()
|
||||
))
|
||||
.map(kestraTestAnnotation -> {
|
||||
var envsSet = new java.util.HashSet<>(Set.of(kestraTestAnnotation.environments()));
|
||||
envsSet.add("test");// add test env if not already present
|
||||
return new MicronautTestValue(
|
||||
kestraTestAnnotation.application(),
|
||||
envsSet.toArray(new String[0]),
|
||||
kestraTestAnnotation.packages(),
|
||||
kestraTestAnnotation.propertySources(),
|
||||
kestraTestAnnotation.rollback(),
|
||||
kestraTestAnnotation.transactional(),
|
||||
kestraTestAnnotation.rebuildContext(),
|
||||
kestraTestAnnotation.contextBuilder(),
|
||||
kestraTestAnnotation.transactionMode(),
|
||||
kestraTestAnnotation.startApplication(),
|
||||
kestraTestAnnotation.resolveParameters()
|
||||
);
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
3108
ui/package-lock.json
generated
3108
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@
|
||||
"@kestra-io/ui-libs": "^0.0.268",
|
||||
"@vue-flow/background": "^1.3.2",
|
||||
"@vue-flow/controls": "^1.1.2",
|
||||
"@vue-flow/core": "^1.48.0",
|
||||
"@vue-flow/core": "^1.48.1",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"axios": "^1.13.2",
|
||||
@@ -39,7 +39,7 @@
|
||||
"cytoscape": "^3.33.0",
|
||||
"dagre": "^0.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"element-plus": "2.12.0",
|
||||
"element-plus": "2.13.0",
|
||||
"humanize-duration": "^3.33.2",
|
||||
"js-yaml": "^4.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -59,15 +59,15 @@
|
||||
"path-browserify": "^1.0.1",
|
||||
"pdfjs-dist": "^5.4.449",
|
||||
"pinia": "^3.0.4",
|
||||
"posthog-js": "^1.308.0",
|
||||
"posthog-js": "^1.310.1",
|
||||
"rapidoc": "^9.3.8",
|
||||
"semver": "^7.7.3",
|
||||
"shiki": "^3.20.0",
|
||||
"vue": "^3.5.25",
|
||||
"vue": "^3.5.26",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vue-chartjs": "^5.3.3",
|
||||
"vue-gtag": "^3.6.3",
|
||||
"vue-i18n": "^11.2.2",
|
||||
"vue-i18n": "^11.2.7",
|
||||
"vue-material-design-icons": "^5.3.1",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue-sidebar-menu": "^5.9.1",
|
||||
@@ -97,9 +97,9 @@
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/testing-library__jest-dom": "^6.0.0",
|
||||
"@types/testing-library__user-event": "^4.2.0",
|
||||
"@typescript-eslint/parser": "^8.50.0",
|
||||
"@typescript-eslint/parser": "^8.50.1",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
@@ -120,29 +120,29 @@
|
||||
"playwright": "^1.55.0",
|
||||
"prettier": "^3.7.4",
|
||||
"rimraf": "^6.1.2",
|
||||
"rolldown-vite": "^7.2.11",
|
||||
"rolldown-vite": "^7.3.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"sass": "^1.97.0",
|
||||
"storybook": "^9.1.16",
|
||||
"sass": "^1.97.1",
|
||||
"storybook": "^9.1.17",
|
||||
"storybook-vue3-router": "^6.0.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.50.0",
|
||||
"typescript-eslint": "^8.50.1",
|
||||
"uuid": "^13.0.0",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"vitest": "^3.2.4",
|
||||
"vue-tsc": "^3.1.8"
|
||||
"vue-tsc": "^3.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.27.2",
|
||||
"@esbuild/darwin-x64": "^0.27.2",
|
||||
"@esbuild/linux-x64": "^0.27.2",
|
||||
"@rollup/rollup-darwin-arm64": "^4.53.5",
|
||||
"@rollup/rollup-darwin-x64": "^4.53.5",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.53.5",
|
||||
"@swc/core-darwin-arm64": "^1.15.5",
|
||||
"@swc/core-darwin-x64": "^1.15.5",
|
||||
"@swc/core-linux-x64-gnu": "^1.15.5"
|
||||
"@rollup/rollup-darwin-arm64": "^4.54.0",
|
||||
"@rollup/rollup-darwin-x64": "^4.54.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.54.0",
|
||||
"@swc/core-darwin-arm64": "^1.15.7",
|
||||
"@swc/core-darwin-x64": "^1.15.7",
|
||||
"@swc/core-linux-x64-gnu": "^1.15.7"
|
||||
},
|
||||
"overrides": {
|
||||
"bootstrap": {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
REF_PATH_INJECTION_KEY,
|
||||
ROOT_SCHEMA_INJECTION_KEY,
|
||||
SCHEMA_DEFINITIONS_INJECTION_KEY,
|
||||
UPDATE_TASK_FUNCTION_INJECTION_KEY
|
||||
UPDATE_YAML_FUNCTION_INJECTION_KEY
|
||||
} from "../../no-code/injectionKeys";
|
||||
import {NoCodeProps} from "../../flows/noCodeTypes";
|
||||
import {deepEqual} from "../../../utils/utils";
|
||||
@@ -68,7 +68,7 @@
|
||||
dashboardStore.sourceCode = YAML_UTILS.stringify(app);
|
||||
}
|
||||
|
||||
provide(UPDATE_TASK_FUNCTION_INJECTION_KEY, (yaml) => {
|
||||
provide(UPDATE_YAML_FUNCTION_INJECTION_KEY, (yaml) => {
|
||||
editorUpdate(yaml)
|
||||
})
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
import {Chart, getDashboard} from "../composables/useDashboards";
|
||||
import {useChartGenerator} from "../composables/useDashboards";
|
||||
|
||||
|
||||
import {useBreakpoints, breakpointsElement} from "@vueuse/core";
|
||||
const verticalLayout = useBreakpoints(breakpointsElement).smallerOrEqual("sm");
|
||||
|
||||
import {customBarLegend} from "../composables/useLegend";
|
||||
import {useTheme} from "../../../utils/utils";
|
||||
@@ -102,7 +103,7 @@
|
||||
beginAtZero: true,
|
||||
position: "left",
|
||||
...DEFAULTS,
|
||||
display: props.short ? false : true,
|
||||
display: verticalLayout.value ? false : (props.short ? false : true),
|
||||
ticks: {
|
||||
...DEFAULTS.ticks,
|
||||
callback: value => isDurationAgg() ? Utils.humanDuration(value) : value
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
import KestraUtils, {useTheme} from "../../../utils/utils";
|
||||
import {FilterObject} from "../../../utils/filters";
|
||||
|
||||
import {useBreakpoints, breakpointsElement} from "@vueuse/core";
|
||||
const verticalLayout = useBreakpoints(breakpointsElement).smallerOrEqual("sm");
|
||||
|
||||
import {useI18n} from "vue-i18n";
|
||||
const {t} = useI18n();
|
||||
|
||||
@@ -129,7 +132,7 @@
|
||||
},
|
||||
position: "left",
|
||||
...DEFAULTS,
|
||||
display: props.short || props.execution ? false : true,
|
||||
display: verticalLayout.value ? false : (props.short || props.execution ? false : true),
|
||||
ticks: {
|
||||
...DEFAULTS.ticks,
|
||||
callback: (value: any) => isDuration(aggregator.value[0]?.[1]?.field) ? KestraUtils.humanDuration(value) : value
|
||||
@@ -143,7 +146,7 @@
|
||||
},
|
||||
position: "right",
|
||||
...DEFAULTS,
|
||||
display: props.short ? false : true,
|
||||
display: verticalLayout.value ? false : (props.short ? false : true),
|
||||
ticks: {
|
||||
...DEFAULTS.ticks,
|
||||
callback: (value: any) => isDuration(aggregator.value[1]?.[1]?.field) ? KestraUtils.humanDuration(value) : value
|
||||
|
||||
@@ -25,21 +25,6 @@
|
||||
<Row :rows="general" />
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div class="labels">
|
||||
<Row :rows="[{icon: LabelMultiple, label: $t('labels')}]">
|
||||
<template #action>
|
||||
<SetLabels :execution />
|
||||
</template>
|
||||
</Row>
|
||||
<Labels :labels="execution.labels || []" />
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div class="metadata">
|
||||
<Row :rows="metadata" />
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div class="actions">
|
||||
<Row
|
||||
@@ -60,6 +45,21 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div class="metadata">
|
||||
<Row :rows="metadata" />
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div class="labels">
|
||||
<Row :rows="[{icon: LabelMultiple, label: $t('labels')}]">
|
||||
<template #action>
|
||||
<SetLabels :execution />
|
||||
</template>
|
||||
</Row>
|
||||
<Labels :labels="execution.labels || []" />
|
||||
</div>
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
|
||||
import ErrorAlert from "./components/main/ErrorAlert.vue";
|
||||
import Id from "../../Id.vue";
|
||||
import Cascader from "./components/main/cascaders/Cascader.vue";
|
||||
import Cascader, {type Element} from "./components/main/cascaders/Cascader.vue";
|
||||
import TimeSeries from "../../dashboard/sections/TimeSeries.vue";
|
||||
import PrevNext from "./components/main/PrevNext.vue";
|
||||
|
||||
@@ -406,7 +406,7 @@
|
||||
);
|
||||
};
|
||||
|
||||
const cascaders = [
|
||||
const cascaders: Element[] = [
|
||||
{
|
||||
title: t("variables"),
|
||||
empty: t("no_variables"),
|
||||
|
||||
@@ -83,13 +83,20 @@
|
||||
children?: Node[];
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
type DebugTypes = "outputs" | "trigger";
|
||||
|
||||
export type Element = {
|
||||
title: string;
|
||||
empty: string;
|
||||
elements?: Record<string, any>;
|
||||
includeDebug?: "outputs" | "trigger";
|
||||
execution: Execution;
|
||||
}>();
|
||||
includeDebug?: DebugTypes | undefined;
|
||||
}
|
||||
|
||||
const props = defineProps<
|
||||
Element & {
|
||||
execution: Execution;
|
||||
}
|
||||
>();
|
||||
|
||||
const path = ref<string>("");
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<el-tooltip placement="bottom" :content="t('playground.tooltip_persistence')">
|
||||
<el-switch v-model="playgroundStore.enabled" :activeText="t('playground.toggle')" class="toggle" :class="{'is-active': playgroundStore.enabled}" />
|
||||
<el-tooltip placement="bottom" :content="$t('playground.tooltip_persistence')">
|
||||
<el-switch v-model="playgroundStore.enabled" :activeText="$t('playground.toggle')" class="toggle" :class="{'is-active': playgroundStore.enabled}" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {usePlaygroundStore} from "../../stores/playground";
|
||||
|
||||
const {t} = useI18n();
|
||||
const playgroundStore = usePlaygroundStore();
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<Keyboard />
|
||||
</el-icon>
|
||||
<span class="fs-6">
|
||||
{{ t("editor_shortcuts.label") }}
|
||||
{{ $t("editor_shortcuts.label") }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -27,7 +27,7 @@
|
||||
</template>
|
||||
</div>
|
||||
<div class="text-break">
|
||||
{{ t(command.description) }}
|
||||
{{ $t(command.description) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,11 +35,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useI18n} from "vue-i18n";
|
||||
import Keyboard from "vue-material-design-icons/Keyboard.vue";
|
||||
import {useKeyShortcuts} from "../../utils/useKeyShortcuts";
|
||||
|
||||
const {t} = useI18n();
|
||||
const {isKeyShortcutsDialogShown} = useKeyShortcuts();
|
||||
|
||||
const commands = [
|
||||
|
||||
@@ -97,7 +97,6 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
h,
|
||||
inject,
|
||||
onBeforeUnmount,
|
||||
@@ -126,7 +125,7 @@
|
||||
import uniqBy from "lodash/uniqBy";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {ElDatePicker} from "element-plus";
|
||||
import {Moment} from "moment";
|
||||
import moment, {Moment} from "moment";
|
||||
import PlaceholderContentWidget from "../../composables/monaco/PlaceholderContentWidget";
|
||||
import Utils from "../../utils/utils";
|
||||
import {hashCode} from "../../utils/global";
|
||||
@@ -137,7 +136,6 @@
|
||||
import EditorType = editor.EditorType;
|
||||
import {useRoute} from "vue-router";
|
||||
|
||||
const currentInstance = getCurrentInstance()!;
|
||||
const {t} = useI18n();
|
||||
|
||||
const textAreaValue = computed({
|
||||
@@ -371,8 +369,7 @@
|
||||
}
|
||||
}, {deep: true});
|
||||
|
||||
const nowMoment: Moment = currentInstance.appContext.config.globalProperties.$moment().startOf("day");
|
||||
|
||||
const nowMoment: Moment = moment().startOf("day");
|
||||
function addedSuggestRows(mutations: MutationRecord[]) {
|
||||
return mutations.flatMap(({addedNodes}) => {
|
||||
const nodes = [...addedNodes];
|
||||
@@ -461,7 +458,7 @@
|
||||
endColumn: wordAtPosition?.endColumn ?? position?.column
|
||||
},
|
||||
// We don't use the selectedDate directly because if user modifies the input value directly it doesn't work otherwise
|
||||
text: `${currentInstance.appContext.config.globalProperties.$moment(
|
||||
text: `${moment(
|
||||
datePicker.value!.$el.nextElementSibling.querySelector("input").value
|
||||
).toISOString(true)} `,
|
||||
forceMoveMarkers: true
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<nav class="d-flex align-items-center w-100 gap-3 top-bar">
|
||||
<SidebarToggleButton
|
||||
v-if="layoutStore.sideMenuCollapsed"
|
||||
@toggle="layoutStore.setSideMenuCollapsed(false)"
|
||||
/>
|
||||
<div class="d-flex flex-column flex-grow-1 flex-shrink-1 overflow-hidden top-title">
|
||||
<div class="d-flex align-items-end gap-2">
|
||||
<SidebarToggleButton
|
||||
v-if="layoutStore.sideMenuCollapsed"
|
||||
@toggle="layoutStore.setSideMenuCollapsed(false)"
|
||||
/>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<el-breadcrumb v-if="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, x) in breadcrumb" :key="x" :class="{'pe-none': item.disabled}">
|
||||
|
||||
@@ -43,7 +43,9 @@
|
||||
BLOCK_SCHEMA_PATH_INJECTION_KEY,
|
||||
CLOSE_TASK_FUNCTION_INJECTION_KEY,
|
||||
CREATE_TASK_FUNCTION_INJECTION_KEY,
|
||||
CREATING_FLOW_INJECTION_KEY,
|
||||
CREATING_TASK_INJECTION_KEY,
|
||||
DEFAULT_NAMESPACE_INJECTION_KEY,
|
||||
EDIT_TASK_FUNCTION_INJECTION_KEY,
|
||||
EDITING_TASK_INJECTION_KEY,
|
||||
FIELDNAME_INJECTION_KEY,
|
||||
@@ -55,7 +57,7 @@
|
||||
REF_PATH_INJECTION_KEY,
|
||||
ROOT_SCHEMA_INJECTION_KEY,
|
||||
SCHEMA_DEFINITIONS_INJECTION_KEY,
|
||||
UPDATE_TASK_FUNCTION_INJECTION_KEY,
|
||||
UPDATE_YAML_FUNCTION_INJECTION_KEY,
|
||||
} from "./injectionKeys";
|
||||
import {useFlowFields, SECTIONS_IDS} from "./utils/useFlowFields";
|
||||
import debounce from "lodash/debounce";
|
||||
@@ -65,6 +67,7 @@
|
||||
import {useKeyboardSave} from "./utils/useKeyboardSave";
|
||||
import {deepEqual} from "../../utils/utils";
|
||||
import {useScrollMemory} from "../../composables/useScrollMemory";
|
||||
import {defaultNamespace} from "../../composables/useNamespaces";
|
||||
|
||||
|
||||
const props = defineProps<NoCodeProps>();
|
||||
@@ -166,6 +169,8 @@
|
||||
provide(REF_PATH_INJECTION_KEY, props.refPath);
|
||||
provide(PANEL_INJECTION_KEY, panel)
|
||||
provide(POSITION_INJECTION_KEY, props.position ?? "after");
|
||||
provide(CREATING_FLOW_INJECTION_KEY, flowStore.isCreating ?? false);
|
||||
provide(DEFAULT_NAMESPACE_INJECTION_KEY, computed(() => flowStore.flow?.namespace ?? defaultNamespace() ?? "company.team"));
|
||||
provide(CREATING_TASK_INJECTION_KEY, props.creatingTask);
|
||||
provide(EDITING_TASK_INJECTION_KEY, props.editingTask);
|
||||
provide(FIELDNAME_INJECTION_KEY, props.fieldName);
|
||||
@@ -184,7 +189,7 @@
|
||||
emit("closeTask")
|
||||
})
|
||||
|
||||
provide(UPDATE_TASK_FUNCTION_INJECTION_KEY, (yaml) => {
|
||||
provide(UPDATE_YAML_FUNCTION_INJECTION_KEY, (yaml) => {
|
||||
editorUpdate(yaml)
|
||||
})
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, inject, ref} from "vue";
|
||||
import {BLOCK_SCHEMA_PATH_INJECTION_KEY} from "../../injectionKeys";
|
||||
import {useFlowStore} from "../../../../stores/flow";
|
||||
import Creation from "./taskList/buttons/Creation.vue";
|
||||
import Element from "./taskList/Element.vue";
|
||||
import * as YAML_UTILS from "@kestra-io/ui-libs/flow-yaml-utils";
|
||||
@@ -53,7 +52,7 @@
|
||||
|
||||
import {
|
||||
CREATING_TASK_INJECTION_KEY, FULL_SCHEMA_INJECTION_KEY, FULL_SOURCE_INJECTION_KEY,
|
||||
PARENT_PATH_INJECTION_KEY, REF_PATH_INJECTION_KEY,
|
||||
PARENT_PATH_INJECTION_KEY, REF_PATH_INJECTION_KEY, UPDATE_YAML_FUNCTION_INJECTION_KEY,
|
||||
} from "../../injectionKeys";
|
||||
import {SECTIONS_MAP} from "../../../../utils/constants";
|
||||
import {getValueAtJsonPath} from "../../../../utils/utils";
|
||||
@@ -83,8 +82,6 @@
|
||||
inheritAttrs: false
|
||||
});
|
||||
|
||||
const flowStore = useFlowStore();
|
||||
|
||||
interface Task {
|
||||
id:string,
|
||||
type:string
|
||||
@@ -150,6 +147,8 @@
|
||||
|
||||
const movedIndex = ref(-1);
|
||||
|
||||
const updateYaml = inject(UPDATE_YAML_FUNCTION_INJECTION_KEY, () => {});
|
||||
|
||||
const moveElement = (
|
||||
items: Record<string, any>[] | undefined,
|
||||
elementID: string,
|
||||
@@ -171,7 +170,7 @@
|
||||
movedIndex.value = -1;
|
||||
}, 200);
|
||||
|
||||
flowStore.flowYaml =
|
||||
updateYaml(
|
||||
YAML_UTILS.swapBlocks({
|
||||
source:flow.value,
|
||||
section: SECTIONS_MAP[section.value.toLowerCase() as keyof typeof SECTIONS_MAP],
|
||||
@@ -179,6 +178,7 @@
|
||||
key2:items[newIndex][keyName],
|
||||
keyName,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const fullSchema = inject(FULL_SCHEMA_INJECTION_KEY, ref<Record<string, any>>({}));
|
||||
|
||||
@@ -8,18 +8,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted} from "vue";
|
||||
import {useFlowStore} from "../../../../stores/flow";
|
||||
import {onMounted, inject, computed, provide} from "vue";
|
||||
import NamespaceSelect from "../../../namespaces/components/NamespaceSelect.vue";
|
||||
import {CREATING_FLOW_INJECTION_KEY, DEFAULT_NAMESPACE_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const modelValue = defineModel<string>();
|
||||
|
||||
const flowStore = useFlowStore();
|
||||
|
||||
const isCreating = computed(() => flowStore.isCreating);
|
||||
const isCreating = inject(CREATING_FLOW_INJECTION_KEY, false);
|
||||
const defaultNamespace = inject(DEFAULT_NAMESPACE_INJECTION_KEY, computed(() => ""));
|
||||
provide(DEFAULT_NAMESPACE_INJECTION_KEY, computed(() => modelValue.value || defaultNamespace.value));
|
||||
|
||||
onMounted(() => {
|
||||
const flowNamespace = flowStore.flow?.namespace;
|
||||
const flowNamespace = defaultNamespace.value;
|
||||
if (!modelValue.value && flowNamespace) {
|
||||
modelValue.value = flowNamespace;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,15 @@
|
||||
</Wrapper>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
|
||||
<el-collapse-item name="general" v-if="generalProperties?.length" :title="$t('no_code.sections.general')">
|
||||
<template v-for="[fieldKey, fieldSchema] in generalProperties" :key="fieldKey">
|
||||
<Wrapper>
|
||||
<template #tasks>
|
||||
<TaskObjectField v-bind="fieldProps(fieldKey, fieldSchema)" />
|
||||
</template>
|
||||
</Wrapper>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="deprecated" v-if="deprecatedProperties?.length" :title="$t('no_code.sections.deprecated')">
|
||||
<template v-for="[fieldKey, fieldSchema] in deprecatedProperties" :key="fieldKey">
|
||||
<Wrapper>
|
||||
@@ -125,6 +133,16 @@
|
||||
return value?.$deprecated;
|
||||
}
|
||||
|
||||
function isPartOfGroup(value: any, groups: string[]) {
|
||||
if (value?.allOf) {
|
||||
return value.allOf.some((item: any) => isPartOfGroup(item, groups));
|
||||
}
|
||||
if (value?.anyOf) {
|
||||
return value.anyOf.some((item: any) => isPartOfGroup(item, groups));
|
||||
}
|
||||
return value?.$group && groups.includes(value.$group);
|
||||
}
|
||||
|
||||
const filteredProperties = computed<Entry[]>(() => {
|
||||
const propertiesProc = (props.properties ?? props.schema?.properties);
|
||||
const isOutputsContext = props.root?.startsWith("outputs[") || false;
|
||||
@@ -159,9 +177,17 @@
|
||||
const protectedRequiredProperties = computed<Entry[]>(() => {
|
||||
return requiredProperties.value.length ? requiredProperties.value : sortedProperties.value;
|
||||
});
|
||||
|
||||
const connectionProperties = computed<Entry[]>(() => {
|
||||
return props.merge ? [] : sortedProperties.value.filter(([p, v]) => v && !isRequired(p) && isPartOfGroup(v, ["connection"]));
|
||||
});
|
||||
|
||||
const optionalProperties = computed<Entry[]>(() => {
|
||||
return props.merge ? [] : sortedProperties.value.filter(([p, v]) => v && !isRequired(p) && !isDeprecated(v) && v.$group !== "connection");
|
||||
return props.merge ? [] : sortedProperties.value.filter(([p, v]) => v && !isRequired(p) && !isDeprecated(v) && !isPartOfGroup(v, ["core","connection"]));
|
||||
});
|
||||
|
||||
const generalProperties = computed<Entry[]>(() => {
|
||||
return props.merge ? [] : sortedProperties.value.filter(([p, v]) => v && !isRequired(p) && !isDeprecated(v) && isPartOfGroup(v, ["core"]));
|
||||
});
|
||||
|
||||
const deprecatedProperties = computed<Entry[]>(() => {
|
||||
@@ -169,9 +195,6 @@
|
||||
return props.merge ? [] : sortedProperties.value.filter(([k, v]) => v && isDeprecated(v) && obj[k] !== undefined);
|
||||
});
|
||||
|
||||
const connectionProperties = computed<Entry[]>(() => {
|
||||
return props.merge ? [] : sortedProperties.value.filter(([p, v]) => v && v.$group === "connection" && !isRequired(p));
|
||||
});
|
||||
|
||||
function onInput(value: any) {
|
||||
emit("update:modelValue", collapseEmptyValues(value));
|
||||
|
||||
@@ -24,6 +24,7 @@ export const POSITION_INJECTION_KEY = Symbol("position-injection-key") as Inject
|
||||
* NOTE: different from the `isCreating` flag coming from the store. `isCreating` refers to the Complete flow being in creation
|
||||
*/
|
||||
export const CREATING_TASK_INJECTION_KEY = Symbol("creating-injection-key") as InjectionKey<boolean>
|
||||
export const CREATING_FLOW_INJECTION_KEY = Symbol("creating-flow-injection-key") as InjectionKey<boolean>
|
||||
/**
|
||||
* When creating anew task, allows to specify a field where the new task should be injected.
|
||||
* @example
|
||||
@@ -51,9 +52,9 @@ export const EDIT_TASK_FUNCTION_INJECTION_KEY = Symbol("edit-function-injection-
|
||||
*/
|
||||
export const CLOSE_TASK_FUNCTION_INJECTION_KEY = Symbol("close-function-injection-key") as InjectionKey<() => void>
|
||||
/**
|
||||
* We call this function when a task is changed, as soon as the first click or type is done
|
||||
* Call this function to update the full Yaml content
|
||||
*/
|
||||
export const UPDATE_TASK_FUNCTION_INJECTION_KEY = Symbol("update-function-injection-key") as InjectionKey<(yaml: string) => void>
|
||||
export const UPDATE_YAML_FUNCTION_INJECTION_KEY = Symbol("update-function-injection-key") as InjectionKey<(yaml: string) => void>
|
||||
/**
|
||||
* Set this to override the contents of the no-code editor with a component of your choice
|
||||
* This is used to display the metadata edition inputs
|
||||
@@ -92,4 +93,6 @@ export const SCHEMA_DEFINITIONS_INJECTION_KEY = Symbol("schema-definitions-injec
|
||||
|
||||
export const DATA_TYPES_MAP_INJECTION_KEY = Symbol("data-types-injection-key") as InjectionKey<ComputedRef<Record<string, string[] | undefined>>>
|
||||
|
||||
export const ON_TASK_EDITOR_CLICK_INJECTION_KEY = Symbol("on-task-editor-click-injection-key") as InjectionKey<(elt?: Partial<NoCodeElement>) => void>;
|
||||
export const ON_TASK_EDITOR_CLICK_INJECTION_KEY = Symbol("on-task-editor-click-injection-key") as InjectionKey<(elt?: Partial<NoCodeElement>) => void>;
|
||||
|
||||
export const DEFAULT_NAMESPACE_INJECTION_KEY = Symbol("default-namespace-injection-key") as InjectionKey<ComputedRef<string>>;
|
||||
@@ -16,7 +16,7 @@
|
||||
import {PLUGIN_DEFAULTS_SECTION, SECTIONS_MAP} from "../../../utils/constants";
|
||||
import {
|
||||
CLOSE_TASK_FUNCTION_INJECTION_KEY,
|
||||
UPDATE_TASK_FUNCTION_INJECTION_KEY,
|
||||
UPDATE_YAML_FUNCTION_INJECTION_KEY,
|
||||
FULL_SOURCE_INJECTION_KEY, CREATING_TASK_INJECTION_KEY,
|
||||
PARENT_PATH_INJECTION_KEY, POSITION_INJECTION_KEY,
|
||||
REF_PATH_INJECTION_KEY, EDIT_TASK_FUNCTION_INJECTION_KEY,
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
const fieldName = inject(FIELDNAME_INJECTION_KEY, undefined);
|
||||
const blockSchemaPath = inject(BLOCK_SCHEMA_PATH_INJECTION_KEY, ref(""));
|
||||
const updateTask = inject(UPDATE_TASK_FUNCTION_INJECTION_KEY, () => {})
|
||||
const updateTask = inject(UPDATE_YAML_FUNCTION_INJECTION_KEY, () => {})
|
||||
|
||||
const closeTaskAddition = inject(
|
||||
CLOSE_TASK_FUNCTION_INJECTION_KEY,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #additional-right>
|
||||
<ul>
|
||||
<li>
|
||||
<el-button v-if="canCreate" tag="router-link" :to="{name: 'flows/create', query: {namespace: $route.query.namespace}}" :icon="Plus" type="primary">
|
||||
<el-button v-if="canCreate" tag="router-link" :to="{name: 'flows/create', query: {namespace: $route.query.namespace}}" :icon="Plus" type="secondary">
|
||||
{{ $t('create_flow') }}
|
||||
</el-button>
|
||||
</li>
|
||||
@@ -30,7 +30,7 @@
|
||||
<el-button
|
||||
v-if="isOSS"
|
||||
@click="startTour"
|
||||
:icon="Plus"
|
||||
:icon="Compass"
|
||||
size="large"
|
||||
type="primary"
|
||||
class="px-3 p-4 section-1-link product-link"
|
||||
@@ -39,7 +39,7 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
:icon="Plus"
|
||||
:icon="Compass"
|
||||
tag="router-link"
|
||||
:to="{name: 'flows/create'}"
|
||||
size="large"
|
||||
@@ -74,6 +74,7 @@
|
||||
import {useCoreStore} from "../../stores/core";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import Plus from "vue-material-design-icons/Plus.vue";
|
||||
import Compass from "vue-material-design-icons/Compass.vue";
|
||||
import Play from "vue-material-design-icons/Play.vue";
|
||||
import OnboardingBottom from "override/components/OnboardingBottom.vue";
|
||||
import kestraWelcome from "../../assets/onboarding/kestra_welcome.svg";
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<el-form-item :label="$t('secret.key')" prop="key">
|
||||
<el-input v-model="secret.key" :disabled="secret.update" required />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!secret.update" :label="$t('secret.name')" prop="value">
|
||||
<el-form-item v-if="!secret.update" :label="$t('secret.name')" prop="value" required>
|
||||
<MultilineSecret v-model="secret.value" :placeholder="secretModalTitle" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="secret.update" :label="$t('secret.name')" prop="value">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {computed, watch} from "vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {configureMonacoYaml} from "monaco-yaml";
|
||||
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
registerPebbleAutocompletion
|
||||
} from "./pebbleLanguageConfigurator";
|
||||
import {usePluginsStore} from "../../../stores/plugins";
|
||||
import {useBlueprintsStore} from "../../../stores/blueprints";
|
||||
import {languages} from "monaco-editor/esm/vs/editor/editor.api";
|
||||
import CompletionItem = languages.CompletionItem;
|
||||
|
||||
@@ -34,11 +36,14 @@ export class YamlLanguageConfigurator extends AbstractLanguageConfigurator {
|
||||
}
|
||||
|
||||
async configureLanguage(pluginsStore: ReturnType<typeof usePluginsStore>) {
|
||||
const validateYAML = computed(() => useBlueprintsStore().validateYAML);
|
||||
watch(validateYAML, (shouldValidate) => configureMonacoYaml(monaco, {validate: shouldValidate}));
|
||||
|
||||
configureMonacoYaml(monaco, {
|
||||
enableSchemaRequest: true,
|
||||
hover: localStorage.getItem("hoverTextEditor") === "true",
|
||||
completion: true,
|
||||
validate: true,
|
||||
validate: validateYAML.value ?? true,
|
||||
format: true,
|
||||
schemas: yamlSchemas()
|
||||
});
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<SideBar v-if="menu" :menu :showLink="showLink" @menu-collapse="onCollapse">
|
||||
<SideBar
|
||||
v-if="menu"
|
||||
:menu
|
||||
:showLink
|
||||
@menu-collapse="onCollapse"
|
||||
:class="{overlay: verticalLayout}"
|
||||
>
|
||||
<template #footer>
|
||||
<Auth />
|
||||
</template>
|
||||
@@ -11,6 +17,9 @@
|
||||
import SideBar from "../../components/layout/SideBar.vue";
|
||||
import Auth from "../../override/components/auth/Auth.vue";
|
||||
|
||||
import {useBreakpoints, breakpointsElement} from "@vueuse/core";
|
||||
const verticalLayout = useBreakpoints(breakpointsElement).smallerOrEqual("sm");
|
||||
|
||||
withDefaults(defineProps<{
|
||||
showLink?: boolean
|
||||
}>(), {
|
||||
|
||||
@@ -56,7 +56,10 @@
|
||||
<div v-if="!system && blueprint.tags?.length > 0" class="tags-section">
|
||||
<span v-for="tag in processedTags(blueprint.tags)" :key="tag.original" class="tag-item">{{ tag.display }}</span>
|
||||
</div>
|
||||
<div class="text-section">
|
||||
<div v-if="blueprint.template" class="tags-section">
|
||||
<span class="tag-item">{{ $t('template') }}</span>
|
||||
</div>
|
||||
<div class="text-section">
|
||||
<h3 class="title">
|
||||
{{ blueprint.title ?? blueprint.id }}
|
||||
</h3>
|
||||
@@ -151,6 +154,7 @@
|
||||
id: string;
|
||||
tags: string[];
|
||||
title?: string;
|
||||
template?: Record<string, any>;
|
||||
}[] | undefined>(undefined);
|
||||
const error = ref(false);
|
||||
const icon = {ContentCopy};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Navbar :title="routeInfo.title">
|
||||
<template #additional-right>
|
||||
<Action
|
||||
v-if="canCreate"
|
||||
v-if="!isOSS && canCreate"
|
||||
:label="t('create')"
|
||||
:to="{name: 'namespaces/create', params: {tab: 'edit'}}"
|
||||
/>
|
||||
@@ -142,6 +142,8 @@
|
||||
const systemNamespace = computed(
|
||||
() => miscStore.configs?.systemNamespace || "system",
|
||||
);
|
||||
|
||||
const isOSS = computed(() => useMiscStore().configs?.edition === "OSS")
|
||||
|
||||
const namespacesHierarchy = computed(() => {
|
||||
if (namespaces.value === undefined || namespaces.value.length === 0) {
|
||||
|
||||
@@ -233,7 +233,7 @@ export function useLeftMenu() {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t("tenant_administration"),
|
||||
title: t("tenant.name"),
|
||||
routes: [
|
||||
"admin/stats",
|
||||
"kv",
|
||||
@@ -332,7 +332,7 @@ export function useLeftMenu() {
|
||||
],
|
||||
},
|
||||
{
|
||||
title: t("instance_administration"),
|
||||
title: t("instance"),
|
||||
routes: routeStartWith("admin/instance"),
|
||||
href: {
|
||||
name: "admin/instance",
|
||||
|
||||
@@ -99,7 +99,6 @@ export default [
|
||||
|
||||
//Namespaces
|
||||
{name: "namespaces/list", path: "/:tenant?/namespaces", component: () => import("override/components/namespaces/Namespaces.vue")},
|
||||
{name: "namespaces/create", path: "/:tenant?/namespaces/new/:tab?", component: () => import("../components/namespaces/Namespace.vue")},
|
||||
{name: "namespaces/update", path: "/:tenant?/namespaces/edit/:id/:tab?", component: () => import("../components/namespaces/Namespace.vue")},
|
||||
|
||||
//Docs
|
||||
|
||||
@@ -25,6 +25,8 @@ interface Blueprint {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type TemplateArgument = Record<string, Input>;
|
||||
|
||||
export interface BlueprintTemplate {
|
||||
source: string;
|
||||
templateArguments: Record<string, Input>;
|
||||
@@ -55,6 +57,8 @@ export const useBlueprintsStore = defineStore("blueprints", () => {
|
||||
const source = ref<string | undefined>(undefined);
|
||||
const graph = ref<any | undefined>(undefined);
|
||||
|
||||
const validateYAML = ref<boolean>(true); // Used to enable/disable YAML validation in Monaco editor, for the purpose of Templated Blueprints
|
||||
|
||||
const getBlueprints = async (options: Options) => {
|
||||
const PARAMS = {params: options.params, ...VALIDATE};
|
||||
|
||||
@@ -166,6 +170,8 @@ export const useBlueprintsStore = defineStore("blueprints", () => {
|
||||
source,
|
||||
graph,
|
||||
|
||||
validateYAML,
|
||||
|
||||
getBlueprints,
|
||||
getBlueprint,
|
||||
getBlueprintSource,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
@import "@kestra-io/ui-libs/src/scss/variables.scss";
|
||||
|
||||
#app {
|
||||
.v-sidebar-menu.vsm_expanded.overlay {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.vsm--item {
|
||||
padding: 0 30px;
|
||||
transition: padding 0.2s ease;
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Nach Trigger-ID filtern",
|
||||
"label": "Trigger-ID"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Nach Trigger-Zustand filtern",
|
||||
"disabled": "Deaktiviert",
|
||||
"enabled": "Aktiviert",
|
||||
"label": "Trigger-Zustand"
|
||||
},
|
||||
"update": "Aktualisieren",
|
||||
"value": "Wert",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "oder benutzerdefinierte Dauer eingeben:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "Instanz",
|
||||
"instance_administration": "Instanzverwaltung",
|
||||
"invalid bulk delete": "Ausführungen konnten nicht gelöscht werden",
|
||||
"invalid bulk force run": "Konnte Ausführungen nicht erzwingen",
|
||||
"invalid bulk kill": "Ausführungen konnten nicht beendet werden",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minuten",
|
||||
"missingSource": "Fehlende Quelle",
|
||||
"modify_inputs": "Eingaben ändern",
|
||||
"monogram": "Monogramm",
|
||||
"months": "Monate",
|
||||
"more_actions": "Weitere Aktionen",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Überprüfungen",
|
||||
"concurrency": "Nebenläufigkeit",
|
||||
"disabled": "Deaktiviert",
|
||||
"labels": "Labels",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Weiter",
|
||||
"no_flows": "Keine Flows unter der tutorial Namespace verfügbar.",
|
||||
"previous": "Zurück",
|
||||
"skip": "Tutorial überspringen",
|
||||
"skip": "Produkt-Tour überspringen",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Wir freuen uns, dass Sie hier sind.<br />Lassen Sie uns Ihren ersten Flow erstellen.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Sind Sie sicher, dass Sie die Ausführung <code>{id}</code> fortsetzen möchten?",
|
||||
"resumed done": "Ausführung ist fortgesetzt",
|
||||
"resumed title": "Ausführung <code>{id}</code> fortsetzen",
|
||||
"reuse_original_inputs": "Ursprüngliche Inputs wiederverwenden",
|
||||
"revision": "Revision",
|
||||
"revisions": "Revisionen",
|
||||
"row count": "Zeilenanzahl",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Mandanten"
|
||||
},
|
||||
"tenantId": "Mandanten-ID",
|
||||
"tenant_administration": "Mandantenverwaltung",
|
||||
"test-badge-text": "Test",
|
||||
"test-badge-tooltip": "Diese Ausführung wurde durch einen Test erstellt",
|
||||
"theme": "Modus",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Willkommen bei Kestra!",
|
||||
"welcome aboard content": "Nutzen Sie unsere geführte Tour, um Ihren ersten Flow zu erstellen, und schauen Sie sich Blueprints an, um weitere Beispiele zu finden.",
|
||||
"welcome button create": "Meinen ersten Flow erstellen",
|
||||
"welcome button create": "Produkt-Tour starten",
|
||||
"welcome display require": "Führen Sie Ihren <strong>ersten Flow</strong> aus, um loszulegen",
|
||||
"welcome_page": {
|
||||
"guide": "Benötigen Sie Unterstützung, um Ihren ersten flow auszuführen?",
|
||||
|
||||
@@ -196,6 +196,8 @@
|
||||
"replayed": "Execution is replayed",
|
||||
"replay confirm": "Are you sure you want to replay this execution <code>{id}</code> and create a new one?",
|
||||
"replay with inputs": "Replay with inputs",
|
||||
"reuse_original_inputs": "Reuse original inputs",
|
||||
"modify_inputs": "Modify inputs",
|
||||
"replay the execution": "Replay the execution <code>{executionId}</code> for the flow <code>{flowId}</code>",
|
||||
"prefill inputs": "Prefill",
|
||||
"current": "current",
|
||||
@@ -391,8 +393,6 @@
|
||||
"conditions": "Conditions",
|
||||
"triggerId": "Trigger ID",
|
||||
"tenantId": "Tenant ID",
|
||||
"tenant_administration": "Tenant Administration",
|
||||
"instance_administration": "Instance Administration",
|
||||
"codeDisabled": "Disabled in Flow",
|
||||
"paused": "Paused",
|
||||
"Fold auto": "Editor: automatic fold of multi-lines",
|
||||
@@ -540,7 +540,7 @@
|
||||
"welcome aboard": "\uD83D\uDE80 Welcome to Kestra!",
|
||||
"welcome aboard content": "Use our Guided Tour to create your first flow and check Blueprints to find more examples.",
|
||||
"welcome display require": "Run your <strong>first flow</strong> to get started",
|
||||
"welcome button create": "Create my first flow",
|
||||
"welcome button create": "Start Product Tour",
|
||||
"live help": "Live help",
|
||||
"show task documentation": "Show task documentation",
|
||||
"hide task documentation": "Hide task documentation",
|
||||
@@ -947,7 +947,7 @@
|
||||
"next": "Next",
|
||||
"previous": "Previous",
|
||||
"finish": "Finish",
|
||||
"skip": "Skip Tutorial",
|
||||
"skip": "Skip Product Tour",
|
||||
"no_flows": "No flows available under tutorial namespace.",
|
||||
"steps": {
|
||||
"0": {
|
||||
@@ -1252,7 +1252,8 @@
|
||||
"disabled": "Disabled",
|
||||
"listeners": "Listeners",
|
||||
"taskDefaults": "Task Defaults",
|
||||
"workerGroup": "Worker Group"
|
||||
"workerGroup": "Worker Group",
|
||||
"checks": "Checks"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtrar por trigger ID",
|
||||
"label": "ID de Trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtrar por estado del trigger",
|
||||
"disabled": "Desactivado",
|
||||
"enabled": "Habilitado",
|
||||
"label": "Estado del Trigger"
|
||||
},
|
||||
"update": "Actualizar",
|
||||
"value": "Valor",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "o ingrese duración personalizada:",
|
||||
"inputs": "Entradas",
|
||||
"instance": "Instancia",
|
||||
"instance_administration": "Administración de Instancia",
|
||||
"invalid bulk delete": "No se pudieron eliminar las ejecuciones",
|
||||
"invalid bulk force run": "No se pudo forzar la ejecución de ejecuciones",
|
||||
"invalid bulk kill": "No se pudieron matar las ejecuciones",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minutos",
|
||||
"missingSource": "Fuente faltante",
|
||||
"modify_inputs": "Modificar inputs",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Más acciones",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Comprobaciones",
|
||||
"concurrency": "Concurrente",
|
||||
"disabled": "Desactivado",
|
||||
"labels": "Etiquetas",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Siguiente",
|
||||
"no_flows": "No hay flows disponibles bajo el namespace del tutorial.",
|
||||
"previous": "Anterior",
|
||||
"skip": "Saltar Tutorial",
|
||||
"skip": "Omitir recorrido del producto",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Estamos encantados de tenerte aquí.<br />Vamos a crear tu primer flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "¿Estás seguro de reanudar la ejecución <code>{id}</code>?",
|
||||
"resumed done": "La ejecución se ha reanudado",
|
||||
"resumed title": "Reanudar ejecución <code>{id}</code>",
|
||||
"reuse_original_inputs": "Reutilizar inputs originales",
|
||||
"revision": "Revisión",
|
||||
"revisions": "Revisiones",
|
||||
"row count": "Recuento de filas",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Arrendatarios"
|
||||
},
|
||||
"tenantId": "ID de Mandante",
|
||||
"tenant_administration": "Administración de Mandantes",
|
||||
"test-badge-text": "Prueba",
|
||||
"test-badge-tooltip": "Esta ejecución fue creada por una prueba",
|
||||
"theme": "Tema",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 ¡Bienvenido a Kestra!",
|
||||
"welcome aboard content": "Usa nuestro Tour Guiado para crear tu primer flow y revisa los Blueprints para encontrar más ejemplos.",
|
||||
"welcome button create": "Crear mi primer flow",
|
||||
"welcome button create": "Iniciar Tour del Producto",
|
||||
"welcome display require": "Ejecuta tu <strong>primer flow</strong> para comenzar",
|
||||
"welcome_page": {
|
||||
"guide": "¿Necesitas orientación para ejecutar tu primer flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtrer par trigger ID",
|
||||
"label": "ID du trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtrer par état du trigger",
|
||||
"disabled": "Désactivé",
|
||||
"enabled": "Activé",
|
||||
"label": "État du Trigger"
|
||||
},
|
||||
"update": "Mettre à jour",
|
||||
"value": "Valeur",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "ou saisir une durée personnalisée :",
|
||||
"inputs": "Entrées",
|
||||
"instance": "Instance",
|
||||
"instance_administration": "Administration de l'Instance",
|
||||
"invalid bulk delete": "Impossible de supprimer les exécutions",
|
||||
"invalid bulk force run": "Impossible de forcer l'exécution des exécutions",
|
||||
"invalid bulk kill": "Impossible d'arrêter les exécutions",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Minimum",
|
||||
"minutes": "Minutes",
|
||||
"missingSource": "Source manquant",
|
||||
"modify_inputs": "Modifier les inputs",
|
||||
"monogram": "Monogramme",
|
||||
"months": "Mois",
|
||||
"more_actions": "Plus d'actions",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Vérifications",
|
||||
"concurrency": "Concurrence",
|
||||
"disabled": "Désactivé",
|
||||
"labels": "Étiquettes",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Suivant",
|
||||
"no_flows": "Aucun flux disponible dans l'espace de noms du tutoriel.",
|
||||
"previous": "Précédent",
|
||||
"skip": "Passer le tutoriel",
|
||||
"skip": "Passer la visite guidée du produit",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Nous sommes ravis de vous avoir ici.<br />Créons votre premier flux.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Êtes-vous sur de vouloir reprendre l'exécution <code>{id}</code>?",
|
||||
"resumed done": "L'exécution est reprise",
|
||||
"resumed title": "Reprendre l'exécution <code>{id}</code>",
|
||||
"reuse_original_inputs": "Réutiliser les inputs d'origine",
|
||||
"revision": "Révision",
|
||||
"revisions": "Révisions",
|
||||
"row count": "Nombre de lignes",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Mandants"
|
||||
},
|
||||
"tenantId": "ID du mandant",
|
||||
"tenant_administration": "Administration des Mandants",
|
||||
"test-badge-text": "Test",
|
||||
"test-badge-tooltip": "Cette exécution a été créée par un Test",
|
||||
"theme": "Thème",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Bienvenue à bord !",
|
||||
"welcome aboard content": "Tout est prêt pour Kestra, commencez la création de votre flow et admirez la magie !",
|
||||
"welcome button create": "Créer mon premier flow",
|
||||
"welcome button create": "Démarrer la visite guidée du produit",
|
||||
"welcome display require": "Prêt à commencer à utiliser Kestra ? Créons ensemble <strong>votre premier flow</strong> !",
|
||||
"welcome_page": {
|
||||
"guide": "Besoin d'aide pour exécuter votre premier flow ?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "ट्रिगर ID द्वारा फ़िल्टर करें",
|
||||
"label": "ट्रिगर ID"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "ट्रिगर स्थिति द्वारा फ़िल्टर करें",
|
||||
"disabled": "अक्षम",
|
||||
"enabled": "सक्रिय",
|
||||
"label": "ट्रिगर स्थिति"
|
||||
},
|
||||
"update": "अपडेट",
|
||||
"value": "मान",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "या कस्टम अवधि दर्ज करें:",
|
||||
"inputs": "इनपुट्स",
|
||||
"instance": "इंस्टेंस",
|
||||
"instance_administration": "इंस्टेंस प्रशासन",
|
||||
"invalid bulk delete": "निष्पादन हटाने में असमर्थ",
|
||||
"invalid bulk force run": "निष्पादन को जबरन चलाने में असमर्थ",
|
||||
"invalid bulk kill": "निष्पादन kill करने में असमर्थ",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "न्यूनतम",
|
||||
"minutes": "मिनट्स",
|
||||
"missingSource": "स्रोत अनुपलब्ध",
|
||||
"modify_inputs": "इनपुट्स संशोधित करें",
|
||||
"monogram": "मोनोग्राम",
|
||||
"months": "महीने",
|
||||
"more_actions": "अधिक क्रियाएँ",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "जांचें",
|
||||
"concurrency": "समानांतरता",
|
||||
"disabled": "अक्षम",
|
||||
"labels": "लेबल्स",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "अगला",
|
||||
"no_flows": "ट्यूटोरियल namespace के अंतर्गत कोई flows उपलब्ध नहीं हैं।",
|
||||
"previous": "पिछला",
|
||||
"skip": "ट्यूटोरियल छोड़ें",
|
||||
"skip": "उत्पाद टूर छोड़ें",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "हमें खुशी है कि आप यहाँ हैं।<br />आइए अपना पहला flow बनाएं।",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "क्या आप वाकई निष्पादन <code>{id}</code> को पुनः आरंभ करना चाहते हैं?",
|
||||
"resumed done": "निष्पादन पुनः आरंभ किया गया है",
|
||||
"resumed title": "निष्पादन <code>{id}</code> पुनः आरंभ करें",
|
||||
"reuse_original_inputs": "मूल inputs का पुनः उपयोग करें",
|
||||
"revision": "संशोधन",
|
||||
"revisions": "पुनरीक्षण",
|
||||
"row count": "पंक्ति संख्या",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "मंडल"
|
||||
},
|
||||
"tenantId": "टेनेंट ID",
|
||||
"tenant_administration": "किरायेदार प्रशासन",
|
||||
"test-badge-text": "परीक्षण",
|
||||
"test-badge-tooltip": "यह execution एक Test द्वारा बनाया गया था",
|
||||
"theme": "थीम",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 केस्ट्रा में आपका स्वागत है!",
|
||||
"welcome aboard content": "अपना पहला flow बनाने के लिए हमारे Guided Tour का उपयोग करें और अधिक उदाहरण खोजने के लिए Blueprints देखें।",
|
||||
"welcome button create": "मेरा पहला flow बनाएं",
|
||||
"welcome button create": "उत्पाद यात्रा शुरू करें",
|
||||
"welcome display require": "शुरू करने के लिए अपना <strong>पहला flow</strong> चलाएँ",
|
||||
"welcome_page": {
|
||||
"guide": "क्या आपको अपना पहला flow निष्पादित करने के लिए मार्गदर्शन चाहिए?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtra per trigger ID",
|
||||
"label": "ID del trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtra per stato del trigger",
|
||||
"disabled": "Disabilitato",
|
||||
"enabled": "Abilitato",
|
||||
"label": "Stato del Trigger"
|
||||
},
|
||||
"update": "Aggiorna",
|
||||
"value": "Valore",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "oppure inserisci durata personalizzata:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "Istanza",
|
||||
"instance_administration": "Amministrazione dell'istanza",
|
||||
"invalid bulk delete": "Impossibile eliminare le esecuzioni",
|
||||
"invalid bulk force run": "Impossibile forzare l'esecuzione delle esecuzioni",
|
||||
"invalid bulk kill": "Impossibile kill le esecuzioni",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minuti",
|
||||
"missingSource": "Sorgente mancante",
|
||||
"modify_inputs": "Modifica input",
|
||||
"monogram": "Monogramma",
|
||||
"months": "Mesi",
|
||||
"more_actions": "Altre azioni",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Controlli",
|
||||
"concurrency": "Concorrenza",
|
||||
"disabled": "Disabilitato",
|
||||
"labels": "Etichette",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Successivo",
|
||||
"no_flows": "Nessun flow disponibile sotto il namespace del tutorial.",
|
||||
"previous": "Precedente",
|
||||
"skip": "Salta Tutorial",
|
||||
"skip": "Salta il Tour del Prodotto",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Siamo entusiasti di averti qui.<br />Creiamo il tuo primo flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Sei sicuro di voler riprendere l'esecuzione <code>{id}</code>?",
|
||||
"resumed done": "L'esecuzione è stata ripresa",
|
||||
"resumed title": "Riprendi esecuzione <code>{id}</code>",
|
||||
"reuse_original_inputs": "Riutilizza gli input originali",
|
||||
"revision": "Revisione",
|
||||
"revisions": "Revisions",
|
||||
"row count": "Conteggio righe",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Mandanti"
|
||||
},
|
||||
"tenantId": "ID del Mandante",
|
||||
"tenant_administration": "Amministrazione del Mandante",
|
||||
"test-badge-text": "Test",
|
||||
"test-badge-tooltip": "Questa esecuzione è stata creata da un Test",
|
||||
"theme": "Tema",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Benvenuto su Kestra!",
|
||||
"welcome aboard content": "Usa il nostro Tour Guidato per creare il tuo primo flow e controlla i Blueprints per trovare altri esempi.",
|
||||
"welcome button create": "Crea il mio primo flow",
|
||||
"welcome button create": "Inizia il Tour del Prodotto",
|
||||
"welcome display require": "Esegui il tuo <strong>primo flow</strong> per iniziare",
|
||||
"welcome_page": {
|
||||
"guide": "Hai bisogno di assistenza per eseguire il tuo primo flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "トリガーIDでフィルター",
|
||||
"label": "トリガーID"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "トリガー状態でフィルター",
|
||||
"disabled": "無効",
|
||||
"enabled": "有効",
|
||||
"label": "トリガー状態"
|
||||
},
|
||||
"update": "更新",
|
||||
"value": "値",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "またはカスタム期間を入力してください:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "インスタンス",
|
||||
"instance_administration": "インスタンス管理",
|
||||
"invalid bulk delete": "実行を削除できませんでした",
|
||||
"invalid bulk force run": "実行を強制的に開始できませんでした",
|
||||
"invalid bulk kill": "実行をkillできませんでした",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "分",
|
||||
"missingSource": "ソースが見つかりません",
|
||||
"modify_inputs": "入力を変更",
|
||||
"monogram": "モノグラム",
|
||||
"months": "月",
|
||||
"more_actions": "その他のアクション",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "チェック",
|
||||
"concurrency": "並行性",
|
||||
"disabled": "無効",
|
||||
"labels": "ラベル",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "次へ",
|
||||
"no_flows": "チュートリアルnamespaceに利用可能なflowはありません。",
|
||||
"previous": "前へ",
|
||||
"skip": "チュートリアルをスキップ",
|
||||
"skip": "プロダクトツアーをスキップ",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "ここに来てくれてとても嬉しいです。<br />最初のflowを作成しましょう。",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "実行<code>{id}</code>を再開してもよろしいですか?",
|
||||
"resumed done": "実行が再開されました",
|
||||
"resumed title": "実行<code>{id}</code>を再開",
|
||||
"reuse_original_inputs": "元のinputを再利用",
|
||||
"revision": "リビジョン",
|
||||
"revisions": "リビジョン",
|
||||
"row count": "行数",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "テナント"
|
||||
},
|
||||
"tenantId": "テナントID",
|
||||
"tenant_administration": "テナント管理",
|
||||
"test-badge-text": "テスト",
|
||||
"test-badge-tooltip": "この実行はテストによって作成されました",
|
||||
"theme": "テーマ",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Kestraへようこそ!",
|
||||
"welcome aboard content": "ガイド付きツアーを利用して最初のflowを作成し、Blueprintsでさらに多くの例を見つけてください。",
|
||||
"welcome button create": "最初のflowを作成",
|
||||
"welcome button create": "プロダクトツアーを開始",
|
||||
"welcome display require": "<strong>最初のflow</strong>を実行して始めましょう",
|
||||
"welcome_page": {
|
||||
"guide": "最初のflowを実行するためのガイダンスが必要ですか?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "트리거 ID로 필터링",
|
||||
"label": "트리거 ID"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "트리거 상태별 필터링",
|
||||
"disabled": "비활성화됨",
|
||||
"enabled": "사용 가능",
|
||||
"label": "트리거 상태"
|
||||
},
|
||||
"update": "업데이트",
|
||||
"value": "값",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "또는 사용자 지정 기간 입력:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "인스턴스",
|
||||
"instance_administration": "인스턴스 관리",
|
||||
"invalid bulk delete": "실행을 삭제할 수 없습니다",
|
||||
"invalid bulk force run": "실행을 강제로 실행할 수 없습니다.",
|
||||
"invalid bulk kill": "실행을 강제 종료할 수 없습니다",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "분",
|
||||
"missingSource": "소스 누락",
|
||||
"modify_inputs": "입력 수정",
|
||||
"monogram": "모노그램",
|
||||
"months": "개월",
|
||||
"more_actions": "추가 작업",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "점검",
|
||||
"concurrency": "병행성",
|
||||
"disabled": "비활성화됨",
|
||||
"labels": "레이블",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "다음",
|
||||
"no_flows": "튜토리얼 namespace에 사용할 수 있는 flow가 없습니다.",
|
||||
"previous": "이전",
|
||||
"skip": "튜토리얼 건너뛰기",
|
||||
"skip": "제품 투어 건너뛰기",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "여기 오신 것을 환영합니다.<br />첫 번째 flow를 만들어 봅시다.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "실행 <code>{id}</code>을(를) 재개하시겠습니까?",
|
||||
"resumed done": "실행이 재개되었습니다.",
|
||||
"resumed title": "실행 <code>{id}</code> 재개",
|
||||
"reuse_original_inputs": "원래 input 재사용",
|
||||
"revision": "수정본",
|
||||
"revisions": "수정본들",
|
||||
"row count": "행 수",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "테넌트"
|
||||
},
|
||||
"tenantId": "테넌트 ID",
|
||||
"tenant_administration": "테넌트 관리",
|
||||
"test-badge-text": "테스트",
|
||||
"test-badge-tooltip": "이 실행은 테스트에 의해 생성되었습니다.",
|
||||
"theme": "테마",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Kestra에 오신 것을 환영합니다!",
|
||||
"welcome aboard content": "가이드 투어를 사용하여 첫 번째 flow를 만들고 Blueprints에서 더 많은 예제를 확인하세요.",
|
||||
"welcome button create": "첫 번째 flow 만들기",
|
||||
"welcome button create": "제품 투어 시작",
|
||||
"welcome display require": "<strong>첫 번째 flow</strong>를 실행하여 시작하세요",
|
||||
"welcome_page": {
|
||||
"guide": "첫 번째 flow를 실행하는 데 도움이 필요하신가요?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtruj według trigger ID",
|
||||
"label": "Identyfikator Trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtruj według stanu triggera",
|
||||
"disabled": "Wyłączone",
|
||||
"enabled": "Włączone",
|
||||
"label": "Stan Trigger"
|
||||
},
|
||||
"update": "Aktualizuj",
|
||||
"value": "Wartość",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "lub wprowadź niestandardowy czas trwania:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "Instancja",
|
||||
"instance_administration": "Administracja Instancji",
|
||||
"invalid bulk delete": "Nie można usunąć wykonań",
|
||||
"invalid bulk force run": "Nie można wymusić uruchomienia wykonania",
|
||||
"invalid bulk kill": "Nie można zabić wykonań",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minuty",
|
||||
"missingSource": "Brak źródła",
|
||||
"modify_inputs": "Modyfikuj inputy",
|
||||
"monogram": "Monogram",
|
||||
"months": "Miesiące",
|
||||
"more_actions": "Więcej akcji",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Kontrole",
|
||||
"concurrency": "Współbieżność",
|
||||
"disabled": "Wyłączony",
|
||||
"labels": "Etykiety",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Następny",
|
||||
"no_flows": "Brak flowów w namespace o nazwie tutorial.",
|
||||
"previous": "Poprzedni",
|
||||
"skip": "Pomiń samouczek",
|
||||
"skip": "Pomiń Przewodnik po Produkcie",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Jesteśmy zachwyceni, że jesteś tutaj.<br />Stwórzmy twój pierwszy flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Czy na pewno chcesz wznowić wykonanie <code>{id}</code>?",
|
||||
"resumed done": "Wykonanie zostało wznowione",
|
||||
"resumed title": "Wznów wykonanie <code>{id}</code>",
|
||||
"reuse_original_inputs": "Ponownie użyj oryginalnych inputów",
|
||||
"revision": "Wersja",
|
||||
"revisions": "Wersje",
|
||||
"row count": "Liczba wierszy",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Najemcy"
|
||||
},
|
||||
"tenantId": "Identyfikator Mandanta",
|
||||
"tenant_administration": "Administracja Mandanta",
|
||||
"test-badge-text": "Test",
|
||||
"test-badge-tooltip": "To wykonanie zostało utworzone przez Test.",
|
||||
"theme": "Motyw",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Witamy w Kestra!",
|
||||
"welcome aboard content": "Skorzystaj z naszego Przewodnika, aby stworzyć swój pierwszy flow i sprawdź Blueprints, aby znaleźć więcej przykładów.",
|
||||
"welcome button create": "Stwórz mój pierwszy flow",
|
||||
"welcome button create": "Rozpocznij Przewodnik po Produkcie",
|
||||
"welcome display require": "Uruchom swój <strong>pierwszy flow</strong>, aby rozpocząć",
|
||||
"welcome_page": {
|
||||
"guide": "Potrzebujesz wskazówek, jak uruchomić swój pierwszy flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtrar por trigger ID",
|
||||
"label": "ID do Trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtrar por estado do trigger",
|
||||
"disabled": "Desativado",
|
||||
"enabled": "Habilitado",
|
||||
"label": "Estado do Trigger"
|
||||
},
|
||||
"update": "Atualizar",
|
||||
"value": "Valor",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "ou insira uma duração personalizada:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "Instância",
|
||||
"instance_administration": "Administração da Instância",
|
||||
"invalid bulk delete": "Não foi possível deletar execuções",
|
||||
"invalid bulk force run": "Não foi possível forçar a execução das execuções",
|
||||
"invalid bulk kill": "Não foi possível matar execuções",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minutos",
|
||||
"missingSource": "Fonte ausente",
|
||||
"modify_inputs": "Modificar inputs",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Mais Ações",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Verificações",
|
||||
"concurrency": "Concorrência",
|
||||
"disabled": "Desativado",
|
||||
"labels": "Etiquetas",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Próximo",
|
||||
"no_flows": "Não há flows disponíveis no namespace do tutorial.",
|
||||
"previous": "Anterior",
|
||||
"skip": "Pular Tutorial",
|
||||
"skip": "Pular Tour do Produto",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Estamos entusiasmados em tê-lo aqui.<br />Vamos criar seu primeiro flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Tem certeza de que deseja retomar a execução <code>{id}</code>?",
|
||||
"resumed done": "Execução foi retomada",
|
||||
"resumed title": "Retomar execução <code>{id}</code>",
|
||||
"reuse_original_inputs": "Reutilizar inputs originais",
|
||||
"revision": "Revisão",
|
||||
"revisions": "Revisões",
|
||||
"row count": "Contagem de linhas",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Mandantes"
|
||||
},
|
||||
"tenantId": "ID do Mandante",
|
||||
"tenant_administration": "Administração do Mandante",
|
||||
"test-badge-text": "Teste",
|
||||
"test-badge-tooltip": "Esta execução foi criada por um Teste",
|
||||
"theme": "Tema",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Bem-vindo ao Kestra!",
|
||||
"welcome aboard content": "Use nosso Tour Guiado para criar seu primeiro flow e confira os Blueprints para encontrar mais exemplos.",
|
||||
"welcome button create": "Criar meu primeiro flow",
|
||||
"welcome button create": "Iniciar Tour do Produto",
|
||||
"welcome display require": "Execute seu <strong>primeiro flow</strong> para começar",
|
||||
"welcome_page": {
|
||||
"guide": "Precisa de orientação para executar seu primeiro flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Filtrar por trigger ID",
|
||||
"label": "ID do Trigger"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Filtrar por estado do trigger",
|
||||
"disabled": "Desativado",
|
||||
"enabled": "Habilitado",
|
||||
"label": "Estado do Trigger"
|
||||
},
|
||||
"update": "Atualizar",
|
||||
"value": "Valor",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "ou insira uma duração personalizada:",
|
||||
"inputs": "Inputs",
|
||||
"instance": "Instância",
|
||||
"instance_administration": "Administração da Instância",
|
||||
"invalid bulk delete": "Não foi possível excluir execuções",
|
||||
"invalid bulk force run": "Não foi possível forçar a execução das execuções",
|
||||
"invalid bulk kill": "Não foi possível matar execuções",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Min",
|
||||
"minutes": "Minutos",
|
||||
"missingSource": "Fonte ausente",
|
||||
"modify_inputs": "Modificar inputs",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Mais Ações",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Verificações",
|
||||
"concurrency": "Concorrência",
|
||||
"disabled": "Desativado",
|
||||
"labels": "Etiquetas",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Próximo",
|
||||
"no_flows": "Não há flows disponíveis no namespace do tutorial.",
|
||||
"previous": "Anterior",
|
||||
"skip": "Pular Tutorial",
|
||||
"skip": "Pular Tour do Produto",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Estamos entusiasmados em tê-lo aqui.<br />Vamos criar seu primeiro flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Tem certeza de que deseja retomar a execução <code>{id}</code>?",
|
||||
"resumed done": "Execução foi retomada",
|
||||
"resumed title": "Retomar execução <code>{id}</code>",
|
||||
"reuse_original_inputs": "Reutilizar inputs originais",
|
||||
"revision": "Revisão",
|
||||
"revisions": "Revisões",
|
||||
"row count": "Contagem de linhas",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Clientes"
|
||||
},
|
||||
"tenantId": "ID do Cliente",
|
||||
"tenant_administration": "Administração de Tenant",
|
||||
"test-badge-text": "Teste",
|
||||
"test-badge-tooltip": "Esta execução foi criada por um Teste",
|
||||
"theme": "Tema",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Bem-vindo ao Kestra!",
|
||||
"welcome aboard content": "Use nosso Tour Guiado para criar seu primeiro flow e confira os Blueprints para encontrar mais exemplos.",
|
||||
"welcome button create": "Criar meu primeiro flow",
|
||||
"welcome button create": "Iniciar Tour do Produto",
|
||||
"welcome display require": "Execute seu <strong>primeiro flow</strong> para começar",
|
||||
"welcome_page": {
|
||||
"guide": "Precisa de orientação para executar seu primeiro flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "Фильтр по trigger ID",
|
||||
"label": "ID триггера"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "Фильтр по состоянию trigger",
|
||||
"disabled": "Отключено",
|
||||
"enabled": "Включено",
|
||||
"label": "Состояние Trigger"
|
||||
},
|
||||
"update": "Обновить",
|
||||
"value": "Значение",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "или введите пользовательскую продолжительность:",
|
||||
"inputs": "Входные данные",
|
||||
"instance": "Экземпляр",
|
||||
"instance_administration": "Администрирование экземпляра",
|
||||
"invalid bulk delete": "Не удалось удалить выполнения",
|
||||
"invalid bulk force run": "Не удалось принудительно запустить executions",
|
||||
"invalid bulk kill": "Не удалось убить выполнения",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "Мин",
|
||||
"minutes": "Минуты",
|
||||
"missingSource": "Отсутствует источник",
|
||||
"modify_inputs": "Изменить inputs",
|
||||
"monogram": "Монограмма",
|
||||
"months": "Месяцы",
|
||||
"more_actions": "Больше действий",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "Проверки",
|
||||
"concurrency": "Конкурентность",
|
||||
"disabled": "Отключено",
|
||||
"labels": "Метки",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "Далее",
|
||||
"no_flows": "Нет доступных flows в namespace учебника.",
|
||||
"previous": "Назад",
|
||||
"skip": "Пропустить учебник",
|
||||
"skip": "Пропустить ознакомительный тур с продуктом",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "Мы рады видеть вас здесь.<br />Давайте создадим ваш первый flow.",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "Вы уверены, что хотите возобновить выполнение <code>{id}</code>?",
|
||||
"resumed done": "Выполнение возобновлено",
|
||||
"resumed title": "Возобновить выполнение <code>{id}</code>",
|
||||
"reuse_original_inputs": "Повторно использовать оригинальные inputы",
|
||||
"revision": "Редакция",
|
||||
"revisions": "Редакции",
|
||||
"row count": "Количество строк",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "Арендаторы"
|
||||
},
|
||||
"tenantId": "ID арендатора",
|
||||
"tenant_administration": "Администрирование Манданта",
|
||||
"test-badge-text": "Тест",
|
||||
"test-badge-tooltip": "Это выполнение было создано тестом",
|
||||
"theme": "Тема",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 Добро пожаловать в Kestra!",
|
||||
"welcome aboard content": "Используйте наше Руководство, чтобы создать ваш первый flow и ознакомьтесь с Blueprints для поиска дополнительных примеров.",
|
||||
"welcome button create": "Создать мой первый flow",
|
||||
"welcome button create": "Начать ознакомительный тур с продуктом",
|
||||
"welcome display require": "Запустите ваш <strong>первый flow</strong>, чтобы начать",
|
||||
"welcome_page": {
|
||||
"guide": "Нужна помощь в выполнении вашего первого flow?",
|
||||
|
||||
@@ -883,6 +883,12 @@
|
||||
"description": "按 trigger ID 筛选",
|
||||
"label": "触发器 ID"
|
||||
},
|
||||
"triggerState": {
|
||||
"description": "按触发器状态筛选",
|
||||
"disabled": "禁用",
|
||||
"enabled": "启用",
|
||||
"label": "触发状态"
|
||||
},
|
||||
"update": "更新",
|
||||
"value": "值",
|
||||
"workerId": {
|
||||
@@ -1005,7 +1011,6 @@
|
||||
"input_custom_duration": "或输入自定义持续时间:",
|
||||
"inputs": "输入",
|
||||
"instance": "实例",
|
||||
"instance_administration": "实例管理",
|
||||
"invalid bulk delete": "无法删除执行",
|
||||
"invalid bulk force run": "无法强制运行执行",
|
||||
"invalid bulk kill": "无法终止执行",
|
||||
@@ -1082,6 +1087,7 @@
|
||||
"min": "最小",
|
||||
"minutes": "分钟",
|
||||
"missingSource": "缺少源",
|
||||
"modify_inputs": "修改输入",
|
||||
"monogram": "字母组合",
|
||||
"months": "月份",
|
||||
"more_actions": "更多操作",
|
||||
@@ -1221,6 +1227,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"general": {
|
||||
"checks": "检查",
|
||||
"concurrency": "并发性",
|
||||
"disabled": "禁用",
|
||||
"labels": "标签",
|
||||
@@ -1308,7 +1315,7 @@
|
||||
"next": "下一步",
|
||||
"no_flows": "教程命名空间下没有可用的流程。",
|
||||
"previous": "上一步",
|
||||
"skip": "跳过教程",
|
||||
"skip": "跳过产品指南",
|
||||
"steps": {
|
||||
"0": {
|
||||
"content": "我们很高兴你在这里。<br />让我们创建你的第一个流程。",
|
||||
@@ -1468,6 +1475,7 @@
|
||||
"resumed confirm": "确定要恢复执行 <code>{id}</code> 吗?",
|
||||
"resumed done": "执行已恢复",
|
||||
"resumed title": "恢复执行 <code>{id}</code>",
|
||||
"reuse_original_inputs": "重用原始input",
|
||||
"revision": "修订版",
|
||||
"revisions": "修订版",
|
||||
"row count": "行数",
|
||||
@@ -1738,7 +1746,6 @@
|
||||
"names": "租户"
|
||||
},
|
||||
"tenantId": "租户 ID",
|
||||
"tenant_administration": "租户管理",
|
||||
"test-badge-text": "测试",
|
||||
"test-badge-tooltip": "此执行由测试创建",
|
||||
"theme": "主题",
|
||||
@@ -1857,7 +1864,7 @@
|
||||
},
|
||||
"welcome aboard": "🚀 欢迎使用 Kestra!",
|
||||
"welcome aboard content": "使用我们的引导游览来创建你的第一个流程,并查看蓝图以找到更多示例。",
|
||||
"welcome button create": "创建我的第一个流程",
|
||||
"welcome button create": "开始产品导览",
|
||||
"welcome display require": "运行你的 <strong>第一个流程</strong> 以开始",
|
||||
"welcome_page": {
|
||||
"guide": "需要指导来执行您的第一个flow吗?",
|
||||
|
||||
@@ -747,11 +747,13 @@ public class ExecutionController {
|
||||
|
||||
return flowInputOutput.readExecutionInputs(flow, current, inputs)
|
||||
.flatMap(executionInputs -> {
|
||||
Check.Behavior behavior = Check.resolveBehavior(flowService.getFailedChecks(flow, executionInputs));
|
||||
List<Check> failed = flowService.getFailedChecks(flow, executionInputs);
|
||||
Check.Behavior behavior = Check.resolveBehavior(failed);
|
||||
if (Check.Behavior.BLOCK_EXECUTION.equals(behavior)) {
|
||||
return Mono.error(new IllegalArgumentException(
|
||||
"Flow execution blocked: one or more condition checks evaluated to false."
|
||||
));
|
||||
+ "\nFailed checks: " + failed.stream().map(Check::getMessage).collect(Collectors.joining(", ")
|
||||
)));
|
||||
}
|
||||
|
||||
final Execution executionWithInputs = Optional.of(current.withInputs(executionInputs))
|
||||
@@ -1036,9 +1038,9 @@ public class ExecutionController {
|
||||
for (String executionId : executionsId) {
|
||||
Optional<Execution> execution = executionRepository.findById(tenantService.resolveTenant(), executionId);
|
||||
|
||||
if (execution.isPresent() && !execution.get().getState().isFailed()) {
|
||||
if (execution.isPresent() && !execution.get().getState().canBeRestarted()) {
|
||||
invalids.add(ManualConstraintViolation.of(
|
||||
"execution not in state FAILED",
|
||||
"execution not in state PAUSED or terminated",
|
||||
executionId,
|
||||
String.class,
|
||||
"execution",
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.kestra.core.models.Label;
|
||||
import io.kestra.core.models.executions.Execution;
|
||||
import io.kestra.core.models.flows.Flow;
|
||||
import io.kestra.core.models.flows.FlowForExecution;
|
||||
import io.kestra.core.models.flows.check.Check;
|
||||
import io.kestra.core.models.property.Property;
|
||||
import io.kestra.core.models.tasks.TaskForExecution;
|
||||
import io.kestra.core.models.triggers.AbstractTriggerForExecution;
|
||||
@@ -530,6 +531,39 @@ class ExecutionControllerTest {
|
||||
assertThat(csv).contains("id");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBlockExecutionAndThrowCheckErrorMessage() {
|
||||
String namespaceId = "io.othercompany";
|
||||
String flowId = "flowWithCheck";
|
||||
|
||||
createFlowWithFailingCheck(namespaceId, flowId);
|
||||
|
||||
HttpClientResponseException e = assertThrows(
|
||||
HttpClientResponseException.class,
|
||||
() ->
|
||||
client.toBlocking().retrieve(
|
||||
HttpRequest.POST("/api/v1/main/executions/" + namespaceId + "/" + flowId, null),
|
||||
Execution.class
|
||||
)
|
||||
);
|
||||
assertThat(e.getMessage()).contains("No VM provided");
|
||||
}
|
||||
|
||||
void createFlowWithFailingCheck(String namespaceId, String flowId) {
|
||||
Flow create = Flow.builder()
|
||||
.id(flowId)
|
||||
.tenantId(MAIN_TENANT)
|
||||
.namespace(namespaceId)
|
||||
.checks(List.of(Check.builder().condition("{{ [] | length > 0 }}").message("No VM provided").style(Check.Style.ERROR).behavior(Check.Behavior.BLOCK_EXECUTION).build()))
|
||||
.tasks(Collections.singletonList(Return.builder().id("test").type(Return.class.getName()).format(Property.of("test")).build()))
|
||||
.build();
|
||||
|
||||
client.toBlocking().retrieve(
|
||||
HttpRequest.POST("/api/v1/main/flows", create),
|
||||
Flow.class
|
||||
);
|
||||
}
|
||||
|
||||
void createAndExecuteFlow() {
|
||||
String namespaceId = "io.othercompany";
|
||||
String flowId = "flowId";
|
||||
@@ -550,4 +584,5 @@ class ExecutionControllerTest {
|
||||
Execution.class
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,12 +9,4 @@ public class WorkerSecurityService {
|
||||
public State.Type callInSecurityContext(AbstractWorkerCallable callable) {
|
||||
return callable.call();
|
||||
}
|
||||
|
||||
public boolean isInSecurityContext() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public AbstractWorkerCallable getCallable() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user