Compare commits

...

20 Commits

Author SHA1 Message Date
Florian Hussonnois
81817291ee chore: bump version to 0.18.2 2024-08-14 18:22:44 +02:00
brian.mulier
9646a42ea7 fix(tests): modify mocks for taskCommands to match the new implementation 2024-08-14 18:22:44 +02:00
Florian Hussonnois
1253d96c8a chore: make DefaultPluginRegistry extendable 2024-08-14 17:25:06 +02:00
YannC
3e40a56c1c fix(core): change property options to values for multiselect, deprecated the older and will be removed in 0.20 2024-08-14 17:13:34 +02:00
YannC
de2467012c fix(ui): prevent event propagate and provide blueprint source in query (#4651)
close #4487
2024-08-14 17:07:43 +02:00
Miloš Paunović
a38bf61c3b fix(ui): add check if property exists on blueprint details page 2024-08-14 17:02:21 +02:00
brian.mulier
92a323a36c fix(script): include directories in docker task runner volume 2024-08-14 17:01:39 +02:00
Loïc Mathieu
d102033a2f fix(core): if empty then
Fixes #4601
2024-08-14 17:01:10 +02:00
yuri
654d62118c feat(ui): add shortcut to Output Debugger (#4612)
Use Ctrl+Enter to run the debug expression.
2024-08-14 16:52:26 +02:00
Miloš Paunović
f9186b29b4 chore(ui): clean template by removing unused data properties (#4615) 2024-08-14 16:52:15 +02:00
brian.mulier
2ee9b9f069 chore: update version to v0.18.1 2024-08-08 17:57:29 +02:00
Miloš Paunović
d57d9dd3e8 fix(ui): graciously handle situation with no tutorial flows loaded (#4609) 2024-08-08 17:56:45 +02:00
Loïc Mathieu
ae15cef7b7 fix(core,jdbc): PurgeLog with levels in postgres
Fixes #4604
2024-08-08 17:55:56 +02:00
eric
0e46055884 fix(ui): improve date handling per locale (#4600) 2024-08-08 17:55:15 +02:00
Miloš Paunović
f91b070dca chore(ui): only show delete logs button on flow logs tab (#4591) 2024-08-08 17:55:07 +02:00
Miloš Paunović
d964d49861 chore(deps): regular dependency updates (#4577) 2024-08-08 17:54:59 +02:00
Ludovic DEHON
dd45545202 fix(core): remove useless logger on jsonfilter 2024-08-08 17:54:51 +02:00
Florian Hussonnois
8d2589485f fix(core): fix NPE for K/V tasks and WorkingDirectory (#4592)
Fix: #4592
2024-08-08 17:54:42 +02:00
YannC
78069b45f8 chore: update version to v0.18.0 2024-08-06 19:41:13 +02:00
YannC
7a13ed79d8 chore(ci): disabled docker image build 2024-08-06 18:41:01 +02:00
40 changed files with 1691 additions and 973 deletions

View File

@@ -22,12 +22,20 @@ updates:
- "dependency-upgrade"
open-pull-requests-limit: 50
# Maintain dependencies for Npm modules
# Maintain dependencies for NPM modules
- package-ecosystem: "npm"
directory: "/ui"
schedule:
# Check for updates to Npm modules every week
interval: "weekly"
day: "sunday"
time: "09:00"
open-pull-requests-limit: 50
labels:
- "dependency-upgrade"
open-pull-requests-limit: 50
ignore:
# Ignore updates of version 1.x, as we're using beta of 2.x
- dependency-name: "vue-virtual-scroller"
versions: ["1.x"]
# Ignore major versions greater than 8, as it's still known to be flaky
- dependency-name: "eslint"
versions: [">8"]

View File

@@ -117,10 +117,12 @@ jobs:
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
if: ${{ github.event.inputs.skip-test == 'false' || github.event.inputs.skip-test == '' }}
uses: docker/setup-buildx-action@v3
# Docker Build
- name: Build & Export Docker Image
if: ${{ github.event.inputs.skip-test == 'false' || github.event.inputs.skip-test == '' }}
uses: docker/build-push-action@v6
with:
context: .
@@ -275,7 +277,7 @@ jobs:
release:
name: Github Release
runs-on: ubuntu-latest
needs: [ check, check-e2e ]
needs: build-artifacts
if: startsWith(github.ref, 'refs/tags/v')
steps:
# Download Exec
@@ -367,7 +369,7 @@ jobs:
maven:
name: Publish to Maven
runs-on: ubuntu-latest
needs: [check, check-e2e]
needs: [check]
if: github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4

View File

@@ -61,14 +61,13 @@ public class LogEntry implements DeletedInterface, TenantInterface {
@Builder.Default
boolean deleted = false;
public static List<String> findLevelsByMin(Level minLevel) {
public static List<Level> findLevelsByMin(Level minLevel) {
if (minLevel == null) {
return Arrays.stream(Level.values()).map(Enum::name).toList();
return Arrays.asList(Level.values());
}
return Arrays.stream(Level.values())
.filter(level -> level.toInt() >= minLevel.toInt())
.map(Enum::name)
.toList();
}

View File

@@ -6,7 +6,6 @@ import io.kestra.core.models.validations.ManualConstraintViolation;
import io.kestra.core.validations.Regex;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -18,11 +17,19 @@ import java.util.List;
@Getter
@NoArgsConstructor
public class MultiselectInput extends Input<List<String>> implements ItemTypeInterface {
@Schema(
title = "Deprecated, please use `values` instead."
)
// @NotNull
@Deprecated
List<@Regex String> options;
@Schema(
title = "List of values available."
)
@NotNull
List<@Regex String> options;
// FIXME: REMOVE `options` in 0.20 and bring back the NotNull
// @NotNull
List<@Regex String> values;
@Schema(
title = "Type of the different values available.",
@@ -33,10 +40,21 @@ public class MultiselectInput extends Input<List<String>> implements ItemTypeInt
@Override
public void validate(List<String> inputs) throws ConstraintViolationException {
if (values != null && options != null) {
throw ManualConstraintViolation.toConstraintViolationException(
"you can't define both `values` and `options`",
this,
MultiselectInput.class,
getId(),
""
);
}
for(String input : inputs){
if (!options.contains(input)) {
List<@Regex String> finalValues = this.values != null ? this.values : this.options;
if (!finalValues.contains(input)) {
throw ManualConstraintViolation.toConstraintViolationException(
"it must match the values `" + options + "`",
"it must match the values `" + finalValues + "`",
this,
MultiselectInput.class,
getId(),

View File

@@ -40,13 +40,17 @@ public interface TaskCommands {
TargetOS getTargetOS();
default List<Path> relativeWorkingDirectoryFilesPaths() throws IOException {
return this.relativeWorkingDirectoryFilesPaths(false);
}
default List<Path> relativeWorkingDirectoryFilesPaths(boolean includeDirectories) throws IOException {
Path workingDirectory = this.getWorkingDirectory();
if (workingDirectory == null) {
return Collections.emptyList();
}
try (Stream<Path> walk = Files.walk(workingDirectory)) {
Stream<Path> filtered = walk.filter(path -> !Files.isDirectory(path));
Stream<Path> filtered = includeDirectories ? walk : walk.filter(path -> !Files.isDirectory(path));
Path outputDirectory = this.getOutputDirectory();
if (outputDirectory != null) {
filtered = filtered.filter(Predicate.not(path -> path.startsWith(outputDirectory)));

View File

@@ -18,7 +18,7 @@ import java.util.function.Predicate;
* @see io.kestra.core.plugins.serdes.PluginDeserializer
* @see PluginScanner
*/
public final class DefaultPluginRegistry implements PluginRegistry {
public class DefaultPluginRegistry implements PluginRegistry {
private static class LazyHolder {
static final DefaultPluginRegistry INSTANCE = new DefaultPluginRegistry();
@@ -43,7 +43,7 @@ public final class DefaultPluginRegistry implements PluginRegistry {
return instance;
}
private DefaultPluginRegistry() {
protected DefaultPluginRegistry() {
}
private boolean isInitialized() {
@@ -53,7 +53,7 @@ public final class DefaultPluginRegistry implements PluginRegistry {
/**
* Initializes the registry by loading all core plugins.
*/
private void init() {
protected void init() {
if (initialized.compareAndSet(false, true)) {
register(scanner.scan());
}

View File

@@ -144,18 +144,16 @@ public class DefaultRunContext extends RunContext {
@Override
public DefaultRunContext clone() {
DefaultRunContext runContext = new DefaultRunContext();
runContext.variableRenderer = this.variableRenderer;
runContext.applicationContext = this.applicationContext;
runContext.storageInterface = this.storageInterface;
runContext.storage = this.storage;
runContext.variables = new HashMap<>(this.variables);
runContext.metrics = new ArrayList<>();
runContext.meterRegistry = this.meterRegistry;
runContext.workingDir = this.workingDir;
runContext.logger = this.logger;
runContext.metrics = new ArrayList<>();
runContext.storage = this.storage;
runContext.pluginConfiguration = this.pluginConfiguration;
runContext.version = version;
runContext.isInitialized.set(this.isInitialized.get());
if (this.isInitialized.get()) {
//Inject all services
runContext.init(applicationContext);
}
return runContext;
}

View File

@@ -61,6 +61,7 @@ public class Extension extends AbstractExtension {
return operators;
}
@SuppressWarnings("deprecation")
@Override
public Map<String, Filter> getFilters() {
Map<String, Filter> filters = new HashMap<>();
@@ -104,6 +105,7 @@ public class Extension extends AbstractExtension {
return tests;
}
@SuppressWarnings("deprecation")
@Override
public Map<String, Function> getFunctions() {
Map<String, Function> functions = new HashMap<>();

View File

@@ -10,11 +10,8 @@ import java.util.Map;
@Slf4j
@Deprecated
public class JsonFilter extends ToJsonFilter {
@Override
public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) throws PebbleException {
log.warn("The 'json' filter is deprecated, please use 'toJson' instead");
return super.apply(input, args, self, context, lineNumber);
}
}

View File

@@ -9,11 +9,8 @@ import java.util.Map;
@Slf4j
@Deprecated
public class JsonFunction extends FromJsonFunction {
@Override
public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
log.warn("The 'json' function is deprecated, please use 'fromJson' instead");
return super.execute(args, self, context, lineNumber);
}
}

View File

@@ -18,6 +18,7 @@ import io.kestra.core.models.tasks.VoidOutput;
import io.kestra.core.runners.FlowableUtils;
import io.kestra.core.runners.RunContext;
import io.kestra.core.utils.GraphUtils;
import io.kestra.core.utils.ListUtils;
import io.kestra.core.utils.TruthUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
@@ -158,7 +159,7 @@ public class If extends Task implements FlowableTask<VoidOutput> {
@Override
public Optional<State.Type> resolveState(RunContext runContext, Execution execution, TaskRun parentTaskRun) throws IllegalVariableEvaluationException {
List<ResolvedTask> childTask = this.childTasks(runContext, parentTaskRun);
if (childTask == null) {
if (ListUtils.isEmpty(childTask)) {
// no next task to run, we guess the state from the parent task
return Optional.of(execution.guessFinalState(null, parentTaskRun, this.isAllowFailure()));
}

View File

@@ -149,7 +149,7 @@ public abstract class AbstractLogRepositoryTest {
logRepository.save(log1);
logRepository.deleteByQuery(null, "io.kestra.unittest", "flowId", null, null, ZonedDateTime.now().plusMinutes(1));
logRepository.deleteByQuery(null, "io.kestra.unittest", "flowId", List.of(Level.TRACE, Level.DEBUG, Level.INFO), null, ZonedDateTime.now().plusMinutes(1));
find = logRepository.findByExecutionId(null, log1.getExecutionId(), null, Pageable.from(1, 50));
assertThat(find.size(), is(0));

View File

@@ -1,5 +1,5 @@
version=0.18.0-SNAPSHOT
version=0.18.2
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.priority=low
org.gradle.priority=low

View File

@@ -10,6 +10,7 @@ import org.jooq.impl.DSL;
import org.slf4j.event.Level;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -27,10 +28,9 @@ public class PostgresLogRepository extends AbstractJdbcLogRepository {
}
@Override
protected Condition minLevel(Level minLevel) {
protected Condition levelsCondition(List<Level> levels) {
return DSL.condition("level in (" +
LogEntry
.findLevelsByMin(minLevel)
levels
.stream()
.map(s -> "'" + s + "'::log_level")
.collect(Collectors.joining(", ")) +

View File

@@ -441,7 +441,7 @@ public abstract class AbstractJdbcLogRepository extends AbstractJdbcRepository i
}
if (logLevels != null) {
delete = delete.and(field("level").in(logLevels));
delete = delete.and(levelsCondition(logLevels));
}
return delete.execute();
@@ -493,7 +493,11 @@ public abstract class AbstractJdbcLogRepository extends AbstractJdbcRepository i
});
}
protected Condition minLevel(Level minLevel) {
return field("level").in(LogEntry.findLevelsByMin(minLevel));
private Condition minLevel(Level minLevel) {
return levelsCondition(LogEntry.findLevelsByMin(minLevel));
}
protected Condition levelsCondition(List<Level> levels) {
return field("level").in(levels.stream().map(level -> level.name()).toList());
}
}

View File

@@ -342,7 +342,7 @@ public class Docker extends TaskRunner {
CreateContainerResponse exec = container.exec();
logger.debug("Container created: {}", exec.getId());
List<Path> relativeWorkingDirectoryFilesPaths = taskCommands.relativeWorkingDirectoryFilesPaths();
List<Path> relativeWorkingDirectoryFilesPaths = taskCommands.relativeWorkingDirectoryFilesPaths(true);
boolean hasFilesToUpload = !ListUtils.isEmpty(relativeWorkingDirectoryFilesPaths);
boolean hasFilesToDownload = !ListUtils.isEmpty(filesToDownload);
boolean outputDirectoryEnabled = taskCommands.outputDirectoryEnabled();

View File

@@ -201,7 +201,7 @@ public abstract class AbstractTaskRunnerTest {
Mockito.when(commands.getEnableOutputDirectory()).thenReturn(true);
Mockito.when(commands.outputDirectoryEnabled()).thenReturn(true);
Mockito.when(commands.getTimeout()).thenReturn(null);
Mockito.when(commands.relativeWorkingDirectoryFilesPaths()).thenCallRealMethod();
Mockito.when(commands.relativeWorkingDirectoryFilesPaths(true)).thenCallRealMethod();
return commands;
}

View File

@@ -1,5 +1,2 @@
public/vscode/
public/vscode-web/
node/
node_modules/

2274
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,9 @@
{
"name": "kestra",
"version": "0.1.0",
"version": "0.18.0",
"private": true,
"packageManager": "npm@9.8.1",
"type": "module",
"packageManager": "npm@9.9.3",
"scripts": {
"dev": "vite --host",
"build": "vite build --emptyOutDir",
@@ -12,17 +13,17 @@
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix"
},
"dependencies": {
"@kestra-io/ui-libs": "^0.0.53",
"@kestra-io/ui-libs": "^0.0.57",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.39.1",
"@vue-flow/core": "^1.39.3",
"ansi-to-html": "^0.7.2",
"axios": "^1.7.2",
"axios": "^1.7.3",
"bootstrap": "^5.3.3",
"buffer": "^6.0.3",
"chart.js": "^4.4.3",
"chartjs-chart-treemap": "^2.3.1",
"core-js": "^3.37.1",
"core-js": "^3.38.0",
"cronstrue": "^2.50.0",
"dagre": "^0.8.5",
"element-plus": "^2.7.8",
@@ -39,20 +40,18 @@
"moment": "^2.30.1",
"moment-range": "4.0.2",
"moment-timezone": "^0.5.45",
"node-modules-polyfill": "^0.1.4",
"nprogress": "^0.2.0",
"pdfjs-dist": "^4.5.136",
"posthog-js": "^1.150.1",
"posthog-js": "^1.154.5",
"throttle-debounce": "^5.0.2",
"vite-plugin-eslint": "^1.8.1",
"vue": "^3.4.34",
"vue": "^3.4.36",
"vue-axios": "3.5.2",
"vue-chartjs": "^5.3.1",
"vue-gtag": "^2.0.1",
"vue-i18n": "^9.13.1",
"vue-material-design-icons": "^5.3.0",
"vue-router": "^4.4.0",
"vue-sidebar-menu": "^5.4.0",
"vue-router": "^4.4.3",
"vue-sidebar-menu": "^5.4.1",
"vue-virtual-scroller": "^2.0.0-beta.8",
"vue3-popper": "^1.5.0",
"vue3-runtime-template": "^1.0.2",
@@ -62,10 +61,11 @@
"yaml": "^2.5.0"
},
"devDependencies": {
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@rushstack/eslint-patch": "^1.10.4",
"@shikijs/markdown-it": "^1.6.3",
"@typescript-eslint/parser": "^7.17.0",
"@vitejs/plugin-vue": "^5.1.1",
"@shikijs/markdown-it": "^1.12.1",
"@typescript-eslint/parser": "^8.0.1",
"@vitejs/plugin-vue": "^5.1.2",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/test-utils": "^2.4.6",
"decompress": "^4.2.1",
@@ -77,13 +77,14 @@
"prettier": "^3.3.3",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.4",
"sass": "^1.77.8",
"typescript": "^5.5.4",
"vite": "^5.3.5",
"vitest": "^2.0.4"
"vite-plugin-eslint": "^1.8.1",
"vitest": "^2.0.5"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.19.0"
"@rollup/rollup-linux-x64-gnu": "^4.20.0"
},
"overrides": {
"bootstrap": {

30
ui/plugins/commit.ts Normal file
View File

@@ -0,0 +1,30 @@
import type {Plugin} from "vite";
import {execSync} from "child_process";
const getInfo = (formats: string[]): string[] => formats.map(format => execSync(`git log -1 --format=${format}`).toString().trim());
const comment = (message: string, author: string, date: string): string => `
<!--
Last Commit:
${message}
----------
Author: ${author}
Date: ${date}
-->`;
export const commit = (): Plugin => {
const [message, author, date] = getInfo(["%s", "%an", "%cd"]);
return {
name: "commit",
transformIndexHtml: {
order: "pre",
handler(html: string): string {
return comment(message, author, date) + html;
},
},
};
};

View File

@@ -1,29 +0,0 @@
import type {Plugin} from "vite";
import {execSync} from "child_process";
const comment = (hash: string, date: string): string => {
return `
<!--
Last Commit:
URL: https://github.com/kestra-io/kestra/commit/${hash}
Date: ${date}
-->`;
};
export const details = (): Plugin => {
const hash: string = execSync("git rev-parse --short HEAD").toString().trim();
const date: string = execSync("git log -1 --format=%cd").toString().trim();
return {
name: "details",
transformIndexHtml: {
order: "pre",
handler(html: string): string {
return comment(hash, date) + html;
},
},
};
};

View File

@@ -65,6 +65,7 @@
:input="true"
:navbar="false"
:model-value="computedDebugValue"
@confirm="onDebugExpression($event)"
class="w-100"
/>

View File

@@ -59,7 +59,7 @@
async setupFlow() {
if (this.$route.query.copy && this.flow){
this.source = this.flow.source;
} else if (this.$route.query.blueprintId) {
} else if (this.$route.query.blueprintId && this.$route.query.blueprintSource) {
this.source = await this.queryBlueprint(this.$route.query.blueprintId)
} else {
this.source = `id: myflow
@@ -86,7 +86,7 @@ tasks:
};
},
blueprintUri() {
return `${apiUrl(this.$store)}/blueprints/community`
return `${apiUrl(this.$store)}/blueprints/${this.$route.query.blueprintSource}`
},
flowParsed() {
return YamlUtils.parse(this.source);

View File

@@ -77,7 +77,6 @@
import {mapState} from "vuex";
import permission from "../../../models/permission";
import action from "../../../models/action";
import {apiUrl} from "override/utils/route";
export default {
components: {Markdown, CopyToClipboard},
@@ -109,6 +108,10 @@
tab: {
type: String,
default: "community"
},
blueprintBaseUri: {
type: String,
required: true
}
},
methods: {
@@ -134,7 +137,7 @@
validateStatus: (status) => {
return status === 200;
}
})).data;
}))?.data;
} else {
this.flowGraph = await this.$store.dispatch("flow/getGraphFromSourceResponse", {
flow: this.blueprint.flow, config: {
@@ -159,9 +162,6 @@
...YamlUtils.parse(this.blueprint.flow),
source: this.blueprint.flow
}
},
blueprintBaseUri() {
return `${apiUrl(this.$store)}/blueprints/` + (this.embed ? this.tab : (this.$route?.params?.tab ?? "community"));
}
}
};
@@ -257,4 +257,4 @@
}
}
}
</style>
</style>

View File

@@ -96,7 +96,7 @@
components: {
MonacoEditor,
},
emits: ["save", "execute", "focusout", "tab", "update:modelValue", "cursor"],
emits: ["save", "execute", "focusout", "tab", "update:modelValue", "cursor", "confirm"],
editor: undefined,
data() {
return {
@@ -262,6 +262,19 @@
}
});
this.editor.addAction({
id: "confirm",
label: "Confirm",
keybindings: [
KeyMod.CtrlCmd | KeyCode.Enter,
],
contextMenuGroupId: "navigation",
contextMenuOrder: 1.5,
run: (ed) => {
this.$emit("confirm", ed.getValue())
}
});
// TabFocus is global to all editor so revert the behavior on non inputs
this.editor.onDidFocusEditorText?.(() => {
TabFocus.setTabFocusMode(this.input);

View File

@@ -42,7 +42,7 @@
multiple
>
<el-option
v-for="item in input.options"
v-for="item in (input.values ?? input.options)"
:key="item"
:label="item"
:value="item"

View File

@@ -124,6 +124,7 @@
},
computed: {
...mapState("api", ["version"]),
...mapState("core", ["tutorialFlows"]),
...mapGetters("core", ["guidedProperties"]),
...mapGetters("auth", ["user"]),
displayNavBar() {
@@ -131,7 +132,7 @@
},
tourEnabled(){
// Temporary solution to not showing the tour menu item for EE
return !Object.keys(this.user).length
return this.tutorialFlows?.length && !Object.keys(this.user).length
}
},
methods: {

View File

@@ -56,7 +56,7 @@
</div>
</el-card>
<el-button v-if="!isLogsListing && logs !== undefined && logs.length > 0" @click="deleteLogs()" class="mb-3 delete-logs-btn">
<el-button v-if="shouldDisplayDeleteButton && logs !== undefined && logs.length > 0" @click="deleteLogs()" class="mb-3 delete-logs-btn">
<TrashCan class="me-2" />
<span>{{ $t("delete logs") }}</span>
</el-button>
@@ -153,8 +153,8 @@
isFlowEdit() {
return this.$route.name === "flows/update"
},
isLogsListing() {
return this.$route.name === "logs/list"
shouldDisplayDeleteButton() {
return this.$route.name === "flows/update"
},
isNamespaceEdit() {
return this.$route.name === "namespaces/update"

View File

@@ -27,7 +27,7 @@
<div
v-if="currentStep(tour).title"
class="title"
:class="{dark: currentStep(tour).keepDark}"
:class="{dark: currentStep(tour).keepDark, empty: !flows.length}"
>
<div v-if="currentStep(tour).icon">
<img
@@ -35,7 +35,7 @@
:class="{jump: currentStep(tour).jump}"
>
</div>
<span v-html="currentStep(tour).title" />
<span v-html="tour.currentStep === 1 && !flows.length ? t('onboarding.no_flows') : currentStep(tour).title" />
</div>
</template>
<template #content>
@@ -68,10 +68,6 @@
:icons="icons"
:variable="ICON_COLOR"
only-icon
:data-cls="task"
:data-task-index="taskIndex"
:data-key="`flow__${flowIndex}__icon__${taskIndex}`"
/>
</div>
</div>
@@ -99,6 +95,7 @@
![0, 1].includes(tour.currentStep) ||
!tour.isLast
"
:disabled="tour.currentStep === 1 && !flows.length"
@click="
tour.isLast
? finishTour(tour.currentStep)
@@ -147,7 +144,6 @@
import Finish from "./components/buttons/Finish.vue";
import {apiUrl} from "override/utils/route";
import {pageFromRoute} from "../../utils/eventsRouter";
import TaskIcon from "@kestra-io/ui-libs/src/components/misc/TaskIcon.vue";
@@ -208,7 +204,7 @@
};
const activeFlow = ref(0);
const flows = ref([]);
const flows = computed(() => store.state.core.tutorialFlows);
const allTasks = (tasks) => {
const uniqueTypes = new Set();
@@ -300,7 +296,7 @@
name: "flows/update",
params: {
namespace: "tutorial",
id: flows.value[activeFlow.value].id,
id: flows.value[activeFlow.value]?.id,
tab: "editor",
},
});
@@ -312,7 +308,7 @@
store.commit("editor/updateOnboarding"),
store.commit("core/setGuidedProperties", {
tourStarted: true,
template: flows.value[activeFlow.value].id,
template: flows.value[activeFlow.value]?.id,
});
wait(1);
@@ -422,11 +418,7 @@
};
onMounted(() => {
const HTTP = getCurrentInstance()?.appContext.config.globalProperties.$http;
HTTP.get(`${apiUrl(this)}/flows/tutorial`).then(
(response) => (flows.value = response.data),
);
store.dispatch("core/readTutorialFlows");
});
</script>
@@ -530,6 +522,11 @@ $flow-image-size-container: 36px;
font-weight: 500;
color: $color;
&.empty {
font-size: 1.2rem;
margin-bottom: 0;
}
& div {
height: 2rem;
margin-bottom: 1rem;

View File

@@ -2,7 +2,7 @@
<top-nav-bar v-if="!embed" :title="routeInfo.title" />
<blueprints-page-header v-if="!embed" class="ms-0 mw-100" />
<section :class="{'container': !embed}" class="main-container" v-bind="$attrs">
<blueprint-detail v-if="selectedBlueprintId" :embed="embed" :blueprint-id="selectedBlueprintId" @back="selectedBlueprintId = undefined" />
<blueprint-detail v-if="selectedBlueprintId" :embed="embed" :blueprint-id="selectedBlueprintId" @back="selectedBlueprintId = undefined" :blueprint-base-uri="blueprintUri" />
<blueprints-browser @loaded="$emit('loaded', $event)" :class="{'d-none': !!selectedBlueprintId}" :embed="embed" :blueprint-base-uri="blueprintUri" @go-to-detail="blueprintId => selectedBlueprintId = blueprintId" />
</section>
</template>

View File

@@ -72,7 +72,7 @@
{{ $t('copy') }}
</el-button>
</el-tooltip>
<el-button v-else-if="userCanCreateFlow" size="large" text bg @click="blueprintToEditor(blueprint.id)">
<el-button v-else-if="userCanCreateFlow" size="large" text bg @click.prevent.stop="blueprintToEditor(blueprint.id)">
{{ $t('use') }}
</el-button>
</div>
@@ -146,7 +146,7 @@
params: {
tenant: this.$route.params.tenant
},
query: {blueprintId: blueprintId}
query: {blueprintId: blueprintId, blueprintSource: this.blueprintBaseUri.includes("community") ? "community" : "custom"}
});
},
tagsToString(blueprintTags) {
@@ -273,9 +273,6 @@
this.load(this.onDataLoaded);
}
},
blueprintBaseUri() {
this.hardReload();
},
tags() {
if(!Object.prototype.hasOwnProperty.call(this.tags, this.selectedTag)) {
this.selectedTag = 0;

View File

@@ -1,3 +1,5 @@
import {apiUrl} from "override/utils/route";
export default {
namespaced: true,
state: {
@@ -10,7 +12,8 @@ export default {
template: undefined,
},
monacoYamlConfigured: false,
autocompletionSource: undefined
autocompletionSource: undefined,
tutorialFlows: []
},
actions: {
showMessage({commit}, message) {
@@ -21,7 +24,10 @@ export default {
},
isUnsaved({commit}, unsavedChange) {
commit("setUnsavedChange", unsavedChange)
}
},
readTutorialFlows({commit}) {
return this.$http.get(`${apiUrl(this)}/flows/tutorial`).then((response) => commit("setTutorialFlows", response.data))
},
},
mutations: {
setMessage(state, message) {
@@ -41,7 +47,10 @@ export default {
},
setAutocompletionSource(state, autocompletionSource) {
state.autocompletionSource = autocompletionSource
}
},
setTutorialFlows(state, flows) {
state.tutorialFlows = flows
},
},
getters: {
unsavedChange(state) {

View File

@@ -790,6 +790,7 @@
"previous": "Previous",
"finish": "Finish",
"skip": "Skip Tutorial",
"no_flows": "No flows available under tutorial namespace.",
"steps": {
"0": {
"title": "Welcome to Kestra!",

View File

@@ -767,6 +767,7 @@
"previous": "Précédent",
"finish": "Finir",
"skip": "Passer le tutoriel",
"no_flows": "Aucun flux disponible dans l'espace de noms du tutoriel.",
"steps": {
"0": {
"title": "Bienvenue chez Kestra!",

View File

@@ -74,7 +74,7 @@ export function chartClick(moment, router, route, event) {
const query = {};
if (event.date) {
const formattedDate = moment(event.date, "DD/MM/YYYY");
const formattedDate = moment(event.date, moment.localeData().longDateFormat("L"));
query.startDate = formattedDate.toISOString(true);
query.endDate = formattedDate.add(1, "d").toISOString(true);
}

View File

@@ -3,7 +3,7 @@ import {createRouter, createWebHistory} from "vue-router";
import VueGtag from "vue-gtag";
import {createI18n} from "vue-i18n";
import moment from "moment-timezone";
import "moment/locale/fr"
import "moment/dist/locale/fr"
import {extendMoment} from "moment-range";
import VueSidebarMenu from "vue-sidebar-menu";
import {

View File

@@ -3,49 +3,43 @@ import mark from "markdown-it-mark";
import meta from "markdown-it-meta";
import anchor from "markdown-it-anchor";
import container from "markdown-it-container";
import {fromHighlighter} from "@shikijs/markdown-it/core"
import {getHighlighterCore} from "shiki/core"
import {fromHighlighter} from "@shikijs/markdown-it/core";
import {createHighlighterCore} from "shiki/core";
import githubDark from "shiki/themes/github-dark.mjs";
import githubLight from "shiki/themes/github-light.mjs";
import {linkTag} from "./markdown_plugins/link";
let highlighter = null;
async function getHighlighter() {
if (!highlighter) {
highlighter = createHighlighterCore({
langs: [import("shiki/langs/yaml.mjs"), import("shiki/langs/python.mjs"), import("shiki/langs/javascript.mjs")],
themes: [githubDark, githubLight],
loadWasm: import("shiki/wasm"),
});
}
return highlighter;
}
export default class Markdown {
static async render(markdown, options) {
const highlighter = await getHighlighter();
githubDark["colors"]["editor.background"] = "var(--bs-gray-500)";
githubLight["colors"]["editor.background"] = "var(--bs-white)";
const highlighter = await getHighlighterCore({
themes: [
githubDark,
githubLight
],
langs: [
import("shiki/langs/yaml.mjs"),
import("shiki/langs/python.mjs"),
import("shiki/langs/javascript.mjs")
],
loadWasm: import("shiki/wasm")
})
options = options || {}
options = options || {};
const darkTheme = document.getElementsByTagName("html")[0].className.indexOf("dark") >= 0;
// noinspection JSPotentiallyInvalidConstructorUsage
let md = new markdownIt() // jshint ignore:line
let md = new markdownIt()
.use(mark)
.use(meta)
.use(anchor, {
permalink: options.permalink ? anchor.permalink.ariaHidden({
placement: "before"
}) : undefined
})
// if more alert types are used inside the task documentation, they need to be configured here also
.use(anchor, {permalink: options.permalink ? anchor.permalink.ariaHidden({placement: "before"}) : undefined})
.use(container, "warning")
.use(container, "info")
.use(fromHighlighter(highlighter, {
theme: darkTheme ? "github-dark" : "github-light",
}))
.use(fromHighlighter(highlighter, {theme: darkTheme ? "github-dark" : "github-light"}))
.use(linkTag);
md.set({
@@ -56,12 +50,10 @@ export default class Markdown {
typographer: true,
langPrefix: "language-",
quotes: "“”‘’",
})
});
md.renderer.rules.table_open = () => "<table class=\"table\">\n"
md.renderer.rules.table_open = () => "<table class=\"table\">\n";
return md.render(
markdown
);
return md.render(markdown);
}
}

View File

@@ -3,9 +3,10 @@ import {defineConfig} from "vite";
import vue from "@vitejs/plugin-vue";
import {visualizer} from "rollup-plugin-visualizer";
import eslintPlugin from "vite-plugin-eslint";
import * as sass from "sass"
import {filename} from "./plugins/filename"
import {details} from "./plugins/details"
import {commit} from "./plugins/commit"
export default defineConfig({
base: "",
@@ -22,16 +23,18 @@ export default defineConfig({
plugins: [
vue(),
visualizer(),
eslintPlugin({
failOnWarning: true,
failOnError: true
}),
eslintPlugin({failOnWarning: true, failOnError: true}),
filename(),
details()
commit()
],
assetsInclude: ["**/*.md"],
css: {
devSourcemap: true
devSourcemap: true,
preprocessorOptions: {
scss: {
logger: sass.Logger.silent
},
}
},
optimizeDeps: {
include: [

View File

@@ -126,7 +126,7 @@ public class LogController {
@Parameter(description = "The min log level filter") @Nullable @QueryValue Level minLevel
) {
AtomicReference<Runnable> cancel = new AtomicReference<>();
List<String> levels = LogEntry.findLevelsByMin(minLevel);
List<String> levels = LogEntry.findLevelsByMin(minLevel).stream().map(level -> level.name()).toList();
return Flux
.<Event<LogEntry>>create(emitter -> {