mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-25 11:12:12 -05:00
Compare commits
6 Commits
plugin/tem
...
fix/load-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14a1a6c104 | ||
|
|
35bce0b5b8 | ||
|
|
e2b46c4cce | ||
|
|
49264cb7f9 | ||
|
|
82e139de03 | ||
|
|
7d64692f0f |
@@ -97,7 +97,7 @@
|
||||
import {State} from "@kestra-io/ui-libs"
|
||||
import Duration from "../layout/Duration.vue";
|
||||
import Utils from "../../utils/utils";
|
||||
import FlowUtils from "../../utils/flowUtils";
|
||||
import * as FlowUtils from "../../utils/flowUtils";
|
||||
import "vue-virtual-scroller/dist/vue-virtual-scroller.css"
|
||||
import {DynamicScroller, DynamicScrollerItem} from "vue-virtual-scroller";
|
||||
import ChevronRight from "vue-material-design-icons/ChevronRight.vue";
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
import Utils from "../../utils/utils";
|
||||
import LogLine from "../logs/LogLine.vue";
|
||||
import Restart from "./Restart.vue";
|
||||
import LogUtils from "../../utils/logs";
|
||||
import * as LogUtils from "../../utils/logs";
|
||||
import Refresh from "vue-material-design-icons/Refresh.vue";
|
||||
import {mapStores} from "pinia";
|
||||
import {useExecutionsStore} from "../../stores/executions";
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import {State} from "@kestra-io/ui-libs"
|
||||
import FlowUtils from "../../utils/flowUtils";
|
||||
import * as FlowUtils from "../../utils/flowUtils";
|
||||
import * as ExecutionUtils from "../../utils/executionUtils";
|
||||
import InputsForm from "../../components/inputs/InputsForm.vue";
|
||||
import {inputsToFormData} from "../../utils/submitTask";
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
import WorkerInfo from "./WorkerInfo.vue";
|
||||
import AiIcon from "../ai/AiIcon.vue";
|
||||
import {State} from "@kestra-io/ui-libs"
|
||||
import FlowUtils from "../../utils/flowUtils";
|
||||
import * as FlowUtils from "../../utils/flowUtils";
|
||||
import _groupBy from "lodash/groupBy";
|
||||
import {TaskIcon, SECTIONS} from "@kestra-io/ui-libs";
|
||||
import Duration from "../layout/Duration.vue";
|
||||
|
||||
@@ -97,13 +97,11 @@
|
||||
import {useRouter} from "vue-router";
|
||||
import {useVueFlow} from "@vue-flow/core";
|
||||
|
||||
// @ts-expect-error no types for SearchField yet
|
||||
import SearchField from "../layout/SearchField.vue";
|
||||
// @ts-expect-error no types for LogLevelSelector yet
|
||||
import LogLevelSelector from "../logs/LogLevelSelector.vue";
|
||||
// @ts-expect-error no types for TaskRunDetails yet
|
||||
import TaskRunDetails from "../logs/TaskRunDetails.vue";
|
||||
// @ts-expect-error no types for Collapse yet
|
||||
import Collapse from "../layout/Collapse.vue";
|
||||
import Drawer from "../Drawer.vue";
|
||||
import Markdown from "../layout/Markdown.vue";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="py-2 line font-monospace"
|
||||
:class="{['log-border-' + log.level.toLowerCase()]: cursor && log.level !== undefined, ['key-' + $.vnode.key]: true}"
|
||||
:class="{['log-border-' + log.level.toLowerCase()]: cursor && log.level !== undefined}"
|
||||
v-if="filtered"
|
||||
:style="logLineStyle"
|
||||
>
|
||||
@@ -16,7 +16,7 @@
|
||||
:class="{'d-inline-block': metaWithValue.length === 0, 'me-3': metaWithValue.length === 0}"
|
||||
>
|
||||
<span class="header-badge text-secondary">
|
||||
{{ $filters.date(log.timestamp, "iso") }}
|
||||
{{ filters.date(log.timestamp, "iso") }}
|
||||
</span>
|
||||
<span v-for="(meta, x) in metaWithValue" :key="x">
|
||||
<span class="header-badge property">
|
||||
@@ -39,157 +39,132 @@
|
||||
<CopyToClipboard :text="`${log.level} ${log.timestamp} ${log.message}`" link />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, onMounted, watch, nextTick} from "vue";
|
||||
import Convert from "ansi-to-html";
|
||||
import xss from "xss";
|
||||
import * as Markdown from "../../utils/markdown";
|
||||
import MenuRight from "vue-material-design-icons/MenuRight.vue";
|
||||
import linkify from "./linkify";
|
||||
import CopyToClipboard from "../layout/CopyToClipboard.vue";
|
||||
import {useStorage} from "@vueuse/core";
|
||||
import {useRouter} from "vue-router";
|
||||
import * as filters from "../../utils/filters";
|
||||
|
||||
let convert = new Convert();
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
cursor?: boolean,
|
||||
log: Record<string, any>,
|
||||
filter?: string,
|
||||
level?: string,
|
||||
excludeMetas?: string[],
|
||||
title?: boolean
|
||||
}>();
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MenuRight,
|
||||
CopyToClipboard
|
||||
},
|
||||
props: {
|
||||
cursor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
log: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
filter: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
level: {
|
||||
type: String,
|
||||
default: "INFO",
|
||||
},
|
||||
excludeMetas: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
title: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
renderedMarkdown: undefined,
|
||||
logsFontSize: parseInt(localStorage.getItem("logsFontSize") || "12"),
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.renderedMarkdown = await Markdown.render(this.message, {onlyLink: true, html: true});
|
||||
},
|
||||
computed: {
|
||||
logLineStyle() {
|
||||
return {
|
||||
fontSize: `${this.logsFontSize}px`,
|
||||
};
|
||||
},
|
||||
metaWithValue() {
|
||||
const metaWithValue = [];
|
||||
const excludes = [
|
||||
"message",
|
||||
"timestamp",
|
||||
"thread",
|
||||
"taskRunId",
|
||||
"level",
|
||||
"index",
|
||||
"attemptNumber",
|
||||
"executionKind"
|
||||
];
|
||||
excludes.push.apply(excludes, this.excludeMetas);
|
||||
for (const key in this.log) {
|
||||
if (this.log[key] && !excludes.includes(key)) {
|
||||
let meta = {key, value: this.log[key]};
|
||||
if (key === "executionId") {
|
||||
meta["router"] = {
|
||||
name: "executions/update",
|
||||
params: {
|
||||
namespace: this.log["namespace"],
|
||||
flowId: this.log["flowId"],
|
||||
id: this.log[key],
|
||||
},
|
||||
};
|
||||
}
|
||||
// State
|
||||
const renderedMarkdown = ref<string | undefined>(undefined);
|
||||
const logsFontSize = useStorage("logsFontSize", 12);
|
||||
const lineContent = ref<HTMLElement>();
|
||||
|
||||
if (key === "namespace") {
|
||||
meta["router"] = {name: "flows/list", query: {namespace: this.log[key]}};
|
||||
}
|
||||
const convert = new Convert();
|
||||
|
||||
if (key === "flowId") {
|
||||
meta["router"] = {
|
||||
name: "flows/update",
|
||||
params: {namespace: this.log["namespace"], id: this.log[key]},
|
||||
};
|
||||
}
|
||||
// Computed
|
||||
const logLineStyle = computed(() => ({
|
||||
fontSize: `${logsFontSize.value}px`,
|
||||
}));
|
||||
|
||||
metaWithValue.push(meta);
|
||||
}
|
||||
const metaWithValue = computed(() => {
|
||||
const metaWithValue: any[] = [];
|
||||
const excludes = [
|
||||
"message",
|
||||
"timestamp",
|
||||
"thread",
|
||||
"taskRunId",
|
||||
"level",
|
||||
"index",
|
||||
"attemptNumber",
|
||||
"executionKind",
|
||||
...(props.excludeMetas ?? [])
|
||||
];
|
||||
for (const key in props.log) {
|
||||
if (props.log[key] && !excludes.includes(key)) {
|
||||
let meta: any = {key, value: props.log[key]};
|
||||
if (key === "executionId") {
|
||||
meta["router"] = {
|
||||
name: "executions/update",
|
||||
params: {
|
||||
namespace: props.log["namespace"],
|
||||
flowId: props.log["flowId"],
|
||||
id: props.log[key],
|
||||
},
|
||||
};
|
||||
}
|
||||
return metaWithValue;
|
||||
},
|
||||
levelStyle() {
|
||||
const lowerCaseLevel = this.log?.level?.toLowerCase();
|
||||
return {
|
||||
"border-color": `var(--ks-log-border-${lowerCaseLevel})`,
|
||||
"color": `var(--ks-log-content-${lowerCaseLevel})`,
|
||||
"background-color": `var(--ks-log-background-${lowerCaseLevel})`,
|
||||
};
|
||||
},
|
||||
filtered() {
|
||||
return (
|
||||
this.filter === "" || (this.log.message && this.log.message.toLowerCase().includes(this.filter))
|
||||
);
|
||||
},
|
||||
iconColor() {
|
||||
const logLevel = this.log.level?.toLowerCase();
|
||||
return `var(--ks-log-content-${logLevel}) !important`; // Use CSS variable for icon color
|
||||
},
|
||||
message() {
|
||||
let logMessage = !this.log.message
|
||||
? ""
|
||||
: convert.toHtml(
|
||||
xss(this.log.message, {
|
||||
allowList: {span: ["style"]},
|
||||
})
|
||||
);
|
||||
|
||||
logMessage = logMessage.replaceAll(
|
||||
/(['"]?)(https?:\/\/[^'"\s]+)(['"]?)/g,
|
||||
"$1<a href='$2' target='_blank'>$2</a>$3"
|
||||
);
|
||||
return logMessage;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("storage", (event) => {
|
||||
if (event.key === "logsFontSize") {
|
||||
this.logsFontSize = parseInt(event.newValue);
|
||||
if (key === "namespace") {
|
||||
meta["router"] = {name: "flows/list", query: {namespace: props.log[key]}};
|
||||
}
|
||||
});
|
||||
if (key === "flowId") {
|
||||
meta["router"] = {
|
||||
name: "flows/update",
|
||||
params: {namespace: props.log["namespace"], id: props.log[key]},
|
||||
};
|
||||
}
|
||||
metaWithValue.push(meta);
|
||||
}
|
||||
}
|
||||
return metaWithValue;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
linkify(this.$refs.lineContent, this.$router);
|
||||
}, 200);
|
||||
},
|
||||
watch: {
|
||||
renderedMarkdown() {
|
||||
this.$nextTick(() => {
|
||||
linkify(this.$refs.lineContent, this.$router);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
const levelStyle = computed(() => {
|
||||
const lowerCaseLevel = props.log?.level?.toLowerCase();
|
||||
return {
|
||||
"border-color": `var(--ks-log-border-${lowerCaseLevel})`,
|
||||
"color": `var(--ks-log-content-${lowerCaseLevel})`,
|
||||
"background-color": `var(--ks-log-background-${lowerCaseLevel})`,
|
||||
};
|
||||
});
|
||||
|
||||
const filtered = computed(() =>
|
||||
props.filter === "" || (props.log.message && props.log.message.toLowerCase().includes(props.filter ?? ""))
|
||||
);
|
||||
|
||||
const iconColor = computed(() => {
|
||||
const logLevel = props.log.level?.toLowerCase();
|
||||
return `var(--ks-log-content-${logLevel}) !important`;
|
||||
});
|
||||
|
||||
const message = computed(() => {
|
||||
let logMessage = !props.log.message
|
||||
? ""
|
||||
: convert.toHtml(
|
||||
xss(props.log.message, {
|
||||
allowList: {span: ["style"]},
|
||||
})
|
||||
);
|
||||
logMessage = logMessage.replaceAll(
|
||||
/(['"]?)(https?:\/\/[^'"\s]+)(['"]?)/g,
|
||||
"$1<a href='$2' target='_blank'>$2</a>$3"
|
||||
);
|
||||
return logMessage;
|
||||
});
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
linkify(lineContent.value, router);
|
||||
}, 200);
|
||||
});
|
||||
|
||||
watch(renderedMarkdown, () => {
|
||||
nextTick(() => {
|
||||
linkify(lineContent.value, router);
|
||||
});
|
||||
});
|
||||
|
||||
// Initial markdown render
|
||||
(async () => {
|
||||
renderedMarkdown.value = await Markdown.render(message.value, {onlyLink: true, html: true});
|
||||
})();
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
div.line {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
<template #tasks>
|
||||
<TaskObjectField
|
||||
v-bind="v"
|
||||
@update:model-value="(val) => onTaskUpdateField(v.fieldKey, val)"
|
||||
@update:model-value="(val: any) => onTaskUpdateField(v.fieldKey, val)"
|
||||
/>
|
||||
</template>
|
||||
</Wrapper>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div @click="isPlugin && pluginsStore.updateDocumentation(taskObject as Parameters<typeof pluginsStore.updateDocumentation>[0])">
|
||||
<TaskObject
|
||||
<BlockObject
|
||||
v-loading="isLoading"
|
||||
v-if="(selectedTaskType || !isTaskDefinitionBasedOnType) && schema"
|
||||
name="root"
|
||||
@@ -26,6 +26,7 @@
|
||||
@update:model-value="onTaskInput"
|
||||
:schema
|
||||
:properties
|
||||
root=""
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -34,7 +35,6 @@
|
||||
import {computed, inject, onActivated, provide, ref, toRaw, watch} from "vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import * as YAML_UTILS from "@kestra-io/ui-libs/flow-yaml-utils";
|
||||
import TaskObject from "./tasks/TaskObject.vue";
|
||||
import PluginSelect from "../../plugins/PluginSelect.vue";
|
||||
import {NoCodeElement, Schemas} from "../utils/types";
|
||||
import {
|
||||
@@ -50,6 +50,7 @@
|
||||
import {getValueAtJsonPath, resolve$ref} from "../../../utils/utils";
|
||||
import PlaygroundRunTaskButton from "../../inputs/PlaygroundRunTaskButton.vue";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import BlockObject from "./tasks/BlockObject.vue";
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
import {SCHEMA_DEFINITIONS_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const props = defineProps<{
|
||||
root: string,
|
||||
schema: Schema,
|
||||
required?: boolean
|
||||
}>();
|
||||
@@ -162,7 +163,7 @@
|
||||
});
|
||||
|
||||
const currentSchemaType = computed(() =>
|
||||
delayedSelectedSchema.value ? getTaskComponent(currentSchema.value) : undefined
|
||||
delayedSelectedSchema.value ? getTaskComponent(currentSchema.value, props.root, definitions.value) : undefined
|
||||
);
|
||||
|
||||
const isSelectingPlugins = computed(() => schemas.value.length > 4);
|
||||
@@ -47,7 +47,7 @@
|
||||
import Add from "../Add.vue";
|
||||
import getTaskComponent from "./getTaskComponent";
|
||||
import Wrapper from "./Wrapper.vue";
|
||||
import {BLOCK_SCHEMA_PATH_INJECTION_KEY} from "../../injectionKeys";
|
||||
import {BLOCK_SCHEMA_PATH_INJECTION_KEY, SCHEMA_DEFINITIONS_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
defineOptions({inheritAttrs: false});
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
schema: any;
|
||||
modelValue?: (string | number | boolean | undefined)[] | string | number | boolean;
|
||||
required?: boolean;
|
||||
root?: string;
|
||||
root: string;
|
||||
}>(), {
|
||||
modelValue: undefined,
|
||||
schema: () => ({}),
|
||||
@@ -70,8 +70,10 @@
|
||||
root: undefined,
|
||||
});
|
||||
|
||||
const definitions = inject(SCHEMA_DEFINITIONS_INJECTION_KEY, computed(() => ({})));
|
||||
|
||||
const componentType = computed(() => {
|
||||
return getTaskComponent(props.schema.items, props.root);
|
||||
return getTaskComponent(props.schema.items, props.root, definitions.value);
|
||||
});
|
||||
|
||||
const needWrapper = computed(() => {
|
||||
@@ -2,19 +2,21 @@
|
||||
<TaskObject
|
||||
:properties="computedProperties"
|
||||
:schema
|
||||
:root
|
||||
merge
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {computed, inject, ref} from "vue";
|
||||
import TaskObject from "./TaskObject.vue";
|
||||
import TaskObject from "./BlockObject.vue";
|
||||
import {resolve$ref} from "../../../../utils/utils";
|
||||
import {FULL_SCHEMA_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
schema: any,
|
||||
properties?: Record<string, any>,
|
||||
root: string
|
||||
}>(), {
|
||||
properties: undefined,
|
||||
});
|
||||
@@ -67,16 +67,17 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, useTemplateRef, watch} from "vue";
|
||||
import {computed, ref, useTemplateRef, watch, onMounted, h, inject} from "vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {DeleteOutline} from "../../utils/icons";
|
||||
|
||||
import InputText from "../inputs/InputText.vue";
|
||||
import TaskExpression from "./TaskExpression.vue";
|
||||
import TaskExpression from "./BlockExpression.vue";
|
||||
import Add from "../Add.vue";
|
||||
import getTaskComponent from "./getTaskComponent";
|
||||
|
||||
import debounce from "lodash/debounce";
|
||||
import Wrapper from "./Wrapper.vue";
|
||||
import {SCHEMA_DEFINITIONS_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const {t, te} = useI18n();
|
||||
|
||||
@@ -86,20 +87,40 @@
|
||||
|
||||
const valueComponent = useTemplateRef<any[]>("valueComponent");
|
||||
|
||||
const model = defineModel<Record<string, any>>({
|
||||
default: () => ({}),
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: Record<string, any>;
|
||||
schema?: any;
|
||||
root?: string;
|
||||
root: string;
|
||||
disabled?: boolean;
|
||||
}>(), {
|
||||
disabled: false,
|
||||
modelValue: () => ({}),
|
||||
root: undefined,
|
||||
schema: () => ({type: "object"})
|
||||
});
|
||||
|
||||
const definitions = inject(SCHEMA_DEFINITIONS_INJECTION_KEY, computed(() => ({})));
|
||||
|
||||
// this convoluted way of importing the getTaskComponent function
|
||||
// is necessary to avoid circular dependencies
|
||||
// RollDown might fix it down the road but as of now,
|
||||
// TaskDict.vue becomes empty in production builds without this lazy loading
|
||||
const getTaskComponent = ref<(property: any, key: string, definitions: any) => any>(() => {
|
||||
return h("div", "Loading...");
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
getTaskComponent.value = (await import("./getTaskComponent")).default;
|
||||
});
|
||||
|
||||
const componentType = computed(() => {
|
||||
return props.schema.additionalProperties ? getTaskComponent(props.schema.additionalProperties, props.root) : null;
|
||||
return props.schema?.additionalProperties ? getTaskComponent.value?.(
|
||||
props.schema.additionalProperties,
|
||||
props.root,
|
||||
definitions.value
|
||||
) : undefined;
|
||||
});
|
||||
|
||||
const currentValue = ref<[string, any][]>([])
|
||||
@@ -109,7 +130,7 @@
|
||||
const localEdit = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
model,
|
||||
(newValue) => {
|
||||
if(localEdit.value) {
|
||||
return;
|
||||
@@ -139,11 +160,9 @@
|
||||
return;
|
||||
}
|
||||
localEdit.value = true;
|
||||
emit("update:modelValue", Object.fromEntries(currentValue.value.filter(pair => pair[0] !== "" && pair[1] !== undefined)));
|
||||
model.value = Object.fromEntries(currentValue.value.filter(pair => pair[0] !== "" && pair[1] !== undefined));
|
||||
}, 200);
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
function getKey(key: string) {
|
||||
return props.root ? `${props.root}.${key}` : key;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<NamespaceSelect
|
||||
data-type="flow"
|
||||
v-model="model"
|
||||
:readOnly="!flowStore.isCreating"
|
||||
allowCreate
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useFlowStore} from "../../../../stores/flow";
|
||||
import NamespaceSelect from "../../../namespaces/components/NamespaceSelect.vue";
|
||||
|
||||
const flowStore = useFlowStore();
|
||||
|
||||
const model = defineModel<string | undefined>();
|
||||
</script>
|
||||
@@ -59,7 +59,7 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, inject, ref} from "vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import TaskDict from "./TaskDict.vue";
|
||||
import TaskDict from "./BlockDict.vue";
|
||||
import Wrapper from "./Wrapper.vue";
|
||||
import TaskObjectField from "./TaskObjectField.vue";
|
||||
import {collapseEmptyValues} from "./MixinTask";
|
||||
@@ -79,7 +79,7 @@
|
||||
modelValue?: Model;
|
||||
required?: boolean;
|
||||
schema?: Schema;
|
||||
root?: string;
|
||||
root: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -40,7 +40,7 @@
|
||||
import Task from "./MixinTask";
|
||||
import Plus from "vue-material-design-icons/Plus.vue";
|
||||
import Minus from "vue-material-design-icons/Minus.vue";
|
||||
import TaskExpression from "./TaskExpression.vue";
|
||||
import TaskExpression from "./BlockExpression.vue";
|
||||
import {mapStores} from "pinia";
|
||||
import {useCoreStore} from "../../../../stores/core";
|
||||
import axios from "axios";
|
||||
@@ -1,155 +0,0 @@
|
||||
<template>
|
||||
<el-form labelPosition="top">
|
||||
<el-form-item
|
||||
:key="index"
|
||||
:required="isRequired(key)"
|
||||
v-for="(schema, key, index) in properties"
|
||||
>
|
||||
<template #label>
|
||||
<span v-if="required" class="me-1 text-danger">*</span>
|
||||
<span v-if="getKey(key)" class="label">
|
||||
{{
|
||||
getKey(key)
|
||||
.split(".")
|
||||
.map(
|
||||
(word) =>
|
||||
word.charAt(0).toUpperCase() +
|
||||
word.slice(1),
|
||||
)
|
||||
.join(" ")
|
||||
}}
|
||||
</span>
|
||||
<el-tag disableTransitions size="small" class="ms-2 type-tag">
|
||||
{{ getTaskComponent(schema, key, properties).ksTaskName }}
|
||||
</el-tag>
|
||||
<el-tooltip
|
||||
v-if="hasTooltip(schema)"
|
||||
:persistent="false"
|
||||
:hideAfter="0"
|
||||
effect="light"
|
||||
>
|
||||
<template #content>
|
||||
<Markdown
|
||||
class="markdown-tooltip"
|
||||
:source="helpText(schema)"
|
||||
/>
|
||||
</template>
|
||||
<Help class="ms-2" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<component
|
||||
:is="getTaskComponent(schema, key, properties)"
|
||||
:modelValue="getPropertiesValue(key)"
|
||||
@update:model-value="onObjectInput(key, $event)"
|
||||
:root="getKey(key)"
|
||||
:schema="schema"
|
||||
:required="isRequired(key)"
|
||||
:min="getExclusiveMinimum(key)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
import getTaskComponent from "./getTaskComponent";
|
||||
import Help from "vue-material-design-icons/HelpBox.vue";
|
||||
import Markdown from "../../../layout/Markdown.vue";
|
||||
</script>
|
||||
<script>
|
||||
import Task from "./MixinTask";
|
||||
|
||||
export default {
|
||||
name: "TaskBasic",
|
||||
mixins: [Task],
|
||||
emits: ["update:modelValue"],
|
||||
computed: {
|
||||
properties() {
|
||||
if (this.schema) {
|
||||
const properties = this.schema.properties;
|
||||
return this.sortProperties(properties);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getPropertiesValue(properties) {
|
||||
return this.modelValue && this.modelValue[properties]
|
||||
? this.modelValue[properties]
|
||||
: undefined;
|
||||
},
|
||||
sortProperties(properties) {
|
||||
if (!properties) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
return Object.entries(properties)
|
||||
.sort((a, b) => {
|
||||
if (a[0] === "id") {
|
||||
return -1;
|
||||
} else if (b[0] === "id") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const aRequired = (this.schema.required || []).includes(
|
||||
a[0],
|
||||
);
|
||||
const bRequired = (this.schema.required || []).includes(
|
||||
b[0],
|
||||
);
|
||||
|
||||
if (aRequired && !bRequired) {
|
||||
return -1;
|
||||
} else if (!aRequired && bRequired) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const aDefault = "default" in a[1];
|
||||
const bDefault = "default" in b[1];
|
||||
|
||||
if (aDefault && !bDefault) {
|
||||
return 1;
|
||||
} else if (!aDefault && bDefault) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a[0].localeCompare(b[0]);
|
||||
})
|
||||
.reduce((result, entry) => {
|
||||
result[entry[0]] = entry[1];
|
||||
return result;
|
||||
}, {});
|
||||
},
|
||||
onObjectInput(properties, value) {
|
||||
const currentValue = this.modelValue || {};
|
||||
currentValue[properties] = value;
|
||||
this.$emit("update:modelValue", currentValue);
|
||||
},
|
||||
hasTooltip(schema) {
|
||||
return schema.title || schema.description;
|
||||
},
|
||||
helpText(schema) {
|
||||
return (
|
||||
(schema.title ? "**" + schema.title + "**" : "") +
|
||||
(schema.title && schema.description ? "\n" : "") +
|
||||
(schema.description ? schema.description : "")
|
||||
);
|
||||
},
|
||||
getExclusiveMinimum(key) {
|
||||
const property = this.schema.properties[key];
|
||||
const propertyHasExclusiveMinimum =
|
||||
property && property.exclusiveMinimum;
|
||||
return propertyHasExclusiveMinimum
|
||||
? property.exclusiveMinimum
|
||||
: null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../styles/code.scss";
|
||||
|
||||
.type-tag {
|
||||
background-color: var(--ks-tag-background);
|
||||
color: var(--ks-tag-content);
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<TaskBoolean
|
||||
<BlockBoolean
|
||||
v-if="isBoolean"
|
||||
v-bind="componentProps"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TaskBoolean from "./TaskBoolean.vue";
|
||||
import BlockBoolean from "./BlockBoolean.vue";
|
||||
|
||||
interface Props {
|
||||
type?: string
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<NamespaceSelect
|
||||
data-type="flow"
|
||||
:value="modelValue"
|
||||
:readOnly="!isCreating"
|
||||
allowCreate
|
||||
@update:model-value="onInput"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import {mapStores} from "pinia";
|
||||
import Task from "./MixinTask";
|
||||
import NamespaceSelect from "../../../namespaces/components/NamespaceSelect.vue";
|
||||
|
||||
import {useFlowStore} from "../../../../stores/flow";
|
||||
export default {
|
||||
components: {NamespaceSelect},
|
||||
mixins: [Task],
|
||||
created() {
|
||||
const flowNamespace = this.flowStore.flow?.namespace;
|
||||
if (!this.modelValue && flowNamespace) {
|
||||
this.onInput(flowNamespace)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useFlowStore),
|
||||
isCreating() {
|
||||
return this.flowStore.isCreating;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -64,12 +64,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, useTemplateRef} from "vue";
|
||||
import {computed, inject, ref, useTemplateRef} from "vue";
|
||||
import Help from "vue-material-design-icons/Information.vue";
|
||||
import Markdown from "../../../layout/Markdown.vue";
|
||||
import TaskLabelWithBoolean from "./TaskLabelWithBoolean.vue";
|
||||
import ClearButton from "./ClearButton.vue";
|
||||
import getTaskComponent from "./getTaskComponent";
|
||||
import {SCHEMA_DEFINITIONS_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const props = defineProps<{
|
||||
schema: any;
|
||||
@@ -134,8 +135,10 @@
|
||||
return type.value.ksTaskName;
|
||||
})
|
||||
|
||||
const definitions = inject(SCHEMA_DEFINITIONS_INJECTION_KEY, computed(() => ({})));
|
||||
|
||||
const type = computed(() => {
|
||||
return getTaskComponent(props.schema, props.fieldKey)
|
||||
return getTaskComponent(props.schema, props.fieldKey, definitions.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<template>
|
||||
<TaskTask @update:model-value="$emit('update:modelValue', $event)" v-bind="$attrs" :section="SECTIONS.TASK_RUNNERS" />
|
||||
</template>
|
||||
<script setup>
|
||||
import {SECTIONS} from "@kestra-io/ui-libs";
|
||||
import TaskTask from "./TaskTask.vue";
|
||||
|
||||
defineEmits(["update:modelValue"]);
|
||||
</script>
|
||||
@@ -1,9 +1,7 @@
|
||||
import {inject} from "vue";
|
||||
import {pascalCase} from "change-case";
|
||||
import {resolve$ref} from "../../../../utils/utils";
|
||||
import {SCHEMA_DEFINITIONS_INJECTION_KEY} from "../../injectionKeys";
|
||||
|
||||
const TasksComponents = import.meta.glob<{ default: any }>("./Task*.vue", {eager: true});
|
||||
const TasksComponents = import.meta.glob<{ default: any }>("./Block*.vue", {eager: true});
|
||||
|
||||
export interface Schema{
|
||||
$ref?: string;
|
||||
@@ -18,22 +16,21 @@ export interface Schema{
|
||||
items?: Schema;
|
||||
const?: string;
|
||||
format?: string;
|
||||
enum?: string[];
|
||||
}
|
||||
|
||||
function getType(property: any, key?: string): string {
|
||||
const definitionsRef = inject(SCHEMA_DEFINITIONS_INJECTION_KEY);
|
||||
const definitions = definitionsRef?.value;
|
||||
function getType(property: Schema, key: string | undefined, definitions: Record<string, Schema> | undefined): string {
|
||||
if (property.enum !== undefined) {
|
||||
return "enum";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(property, "$ref")) {
|
||||
if (Object.prototype.hasOwnProperty.call(property, "$ref") && property.$ref) {
|
||||
if (property.$ref.includes("tasks.Task")) {
|
||||
return "task"
|
||||
}
|
||||
|
||||
if (property.$ref.includes("tasks.runners.TaskRunner")) {
|
||||
return "task-runner"
|
||||
return "task"
|
||||
}
|
||||
|
||||
if (property.$ref.includes("io.kestra.preload")) {
|
||||
@@ -43,14 +40,14 @@ function getType(property: any, key?: string): string {
|
||||
return "complex";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(property, "allOf")) {
|
||||
if (Object.prototype.hasOwnProperty.call(property, "allOf") && property.allOf) {
|
||||
if (property.allOf.length === 2
|
||||
&& property.allOf[0].$ref && !property.allOf[1].properties) {
|
||||
return "complex";
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(property, "anyOf")) {
|
||||
if (Object.prototype.hasOwnProperty.call(property, "anyOf") && property.anyOf) {
|
||||
if (key === "labels" && property.anyOf.length === 2
|
||||
&& property.anyOf[0].type === "array" && property.anyOf[1].type === "object") {
|
||||
return "dict";
|
||||
@@ -63,10 +60,6 @@ function getType(property: any, key?: string): string {
|
||||
return "any-of";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(property, "additionalProperties")) {
|
||||
return "dict";
|
||||
}
|
||||
|
||||
if (property.type === "integer") {
|
||||
return "number";
|
||||
}
|
||||
@@ -89,7 +82,7 @@ function getType(property: any, key?: string): string {
|
||||
return "subflow-inputs";
|
||||
}
|
||||
|
||||
if (property.type === "array") {
|
||||
if (property.type === "array" && property.items) {
|
||||
const items = definitions ? resolve$ref({definitions: definitions}, property.items) : property.items;
|
||||
if (items?.anyOf?.length === 0 || items?.anyOf?.length > 10 || key === "pluginDefaults" || key === "layout") {
|
||||
return "list";
|
||||
@@ -98,6 +91,10 @@ function getType(property: any, key?: string): string {
|
||||
return "array";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(property, "additionalProperties")) {
|
||||
return "dict";
|
||||
}
|
||||
|
||||
if (property.const) {
|
||||
return "constant"
|
||||
}
|
||||
@@ -106,13 +103,13 @@ function getType(property: any, key?: string): string {
|
||||
return "dict";
|
||||
}
|
||||
|
||||
return property.type || "expression";
|
||||
return typeof property.type === "string" ? property.type : "expression";
|
||||
}
|
||||
|
||||
export default function getTaskComponent(property: any, key?: string): any {
|
||||
const typeString = getType(property, key);
|
||||
export default function getTaskComponent(property: any, key: string, definitions: Record<string, Schema>): any {
|
||||
const typeString = getType(property, key, definitions);
|
||||
const type = pascalCase(typeString);
|
||||
const component = TasksComponents[`./Task${type}.vue`]?.default;
|
||||
const component = TasksComponents[`./Block${type}.vue`]?.default;
|
||||
if (component) {
|
||||
component.ksTaskName = typeString;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
tooltip,
|
||||
getFormat,
|
||||
} from "../dashboard/composables/charts";
|
||||
import Logs from "../../utils/logs";
|
||||
import * as Logs from "../../utils/logs";
|
||||
|
||||
export default defineComponent({
|
||||
components: {Bar},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import Auth from "../../override/components/auth/Auth.vue";
|
||||
|
||||
withDefaults(defineProps<{
|
||||
showLink: boolean
|
||||
showLink?: boolean
|
||||
}>(), {
|
||||
showLink: true
|
||||
});
|
||||
|
||||
@@ -54,7 +54,6 @@ interface FlowValidations {
|
||||
export interface Flow {
|
||||
id: string;
|
||||
namespace: string;
|
||||
disabled?: boolean;
|
||||
source: string;
|
||||
revision?: number;
|
||||
deleted?: boolean;
|
||||
|
||||
@@ -220,13 +220,13 @@ export const usePluginsStore = defineStore("plugins", () => {
|
||||
|
||||
const apiStore = useApiStore();
|
||||
|
||||
const apiPromise = apiStore.pluginIcons().then(response => {
|
||||
const apiPromise = apiStore.pluginIcons().then(async response => {
|
||||
apiIcons.value = response.data ?? {};
|
||||
return response.data;
|
||||
});
|
||||
|
||||
const iconsPromise =
|
||||
axios.get(`${apiUrlWithoutTenants()}/plugins/icons`, {}).then(response => {
|
||||
axios.get(`${apiUrlWithoutTenants()}/plugins/icons`, {}).then(async response => {
|
||||
pluginsIcons.value = response.data ?? {};
|
||||
return pluginsIcons.value;
|
||||
});
|
||||
@@ -241,7 +241,7 @@ export const usePluginsStore = defineStore("plugins", () => {
|
||||
|
||||
function groupIcons() {
|
||||
return axios.get(`${apiUrlWithoutTenants()}/plugins/icons/groups`, {})
|
||||
.then(response => {
|
||||
.then(async response => {
|
||||
return response.data;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
export default class FlowUtils {
|
||||
static findTaskById(flow, taskId) {
|
||||
let result = this.loopOver(flow, (value) => {
|
||||
if (value instanceof Object) {
|
||||
if (value.type !== undefined && value.id === taskId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
}
|
||||
|
||||
static loopOver(item, predicate, result) {
|
||||
if (result === undefined) {
|
||||
result = [];
|
||||
}
|
||||
|
||||
if (predicate(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
item.flatMap(item => this.loopOver(item, predicate, result));
|
||||
} else if (item instanceof Object) {
|
||||
Object.entries(item).flatMap(([_key, value]) => {
|
||||
this.loopOver(value, predicate, result);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
33
ui/src/utils/flowUtils.ts
Normal file
33
ui/src/utils/flowUtils.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export function findTaskById(flow: any, taskId: string) {
|
||||
const result = loopOver(flow, (value: any) => {
|
||||
if (value instanceof Object) {
|
||||
if (value.type !== undefined && value.id === taskId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
}
|
||||
|
||||
export function loopOver(item: any, predicate: (item: any) => boolean, result: any[] | undefined = undefined) {
|
||||
if (result === undefined) {
|
||||
result = [];
|
||||
}
|
||||
|
||||
if (predicate(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
item.flatMap(item => loopOver(item, predicate, result));
|
||||
} else if (item instanceof Object) {
|
||||
Object.entries(item).flatMap(([_key, value]) => {
|
||||
loopOver(value, predicate, result);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import {cssVariable} from "@kestra-io/ui-libs";
|
||||
|
||||
const LEVELS = [
|
||||
"ERROR",
|
||||
"WARN",
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
"TRACE"
|
||||
];
|
||||
|
||||
export default class Logs {
|
||||
static color() {
|
||||
return Object.fromEntries(LEVELS.map(level => [level, cssVariable("--log-chart-" + level.toLowerCase())]));
|
||||
}
|
||||
|
||||
static graphColors(state) {
|
||||
const COLORS = {
|
||||
ERROR: "#AB0009",
|
||||
WARN: "#DD5F00",
|
||||
INFO: "#029E73",
|
||||
DEBUG: "#1761FD",
|
||||
TRACE: "#8405FF",
|
||||
};
|
||||
|
||||
return COLORS[state];
|
||||
}
|
||||
|
||||
static chartColorFromLevel(level, alpha = 1) {
|
||||
const hex = Logs.color()[level];
|
||||
if (!hex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16));
|
||||
return `rgba(${r},${g},${b},${alpha})`;
|
||||
}
|
||||
|
||||
static sort(value) {
|
||||
return Object.keys(value)
|
||||
.sort((a, b) => {
|
||||
return Logs.index(LEVELS, a) - Logs.index(LEVELS, b);
|
||||
})
|
||||
.reduce(
|
||||
(obj, key) => {
|
||||
obj[key] = value[key];
|
||||
return obj;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
static index(based, value) {
|
||||
const index = based.indexOf(value);
|
||||
|
||||
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
|
||||
}
|
||||
|
||||
static levelOrLower(level) {
|
||||
const levels = [];
|
||||
for (const currentLevel of LEVELS) {
|
||||
levels.push(currentLevel);
|
||||
if (currentLevel === level) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return levels.reverse();
|
||||
}
|
||||
}
|
||||
66
ui/src/utils/logs.ts
Normal file
66
ui/src/utils/logs.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {cssVariable} from "@kestra-io/ui-libs";
|
||||
|
||||
const LEVELS = [
|
||||
"ERROR",
|
||||
"WARN",
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
"TRACE"
|
||||
];
|
||||
|
||||
export function color() {
|
||||
return Object.fromEntries(LEVELS.map(level => [level, cssVariable("--log-chart-" + level.toLowerCase())])) as Record<typeof LEVELS[number], string>;
|
||||
}
|
||||
|
||||
const COLORS: Record<typeof LEVELS[number], string> = {
|
||||
ERROR: "#AB0009",
|
||||
WARN: "#DD5F00",
|
||||
INFO: "#029E73",
|
||||
DEBUG: "#1761FD",
|
||||
TRACE: "#8405FF",
|
||||
};
|
||||
|
||||
export function graphColors(state: typeof LEVELS[number]) {
|
||||
return COLORS[state];
|
||||
}
|
||||
|
||||
export function chartColorFromLevel(level: typeof LEVELS[number], alpha = 1) {
|
||||
const hex = color()[level];
|
||||
if (!hex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [r, g, b] = hex.match(/\w\w/g)?.map(x => parseInt(x, 16)) ?? [];
|
||||
return `rgba(${r},${g},${b},${alpha})`;
|
||||
}
|
||||
|
||||
export function sort(value: Record<string, any>) {
|
||||
return Object.keys(value)
|
||||
.sort((a, b) => {
|
||||
return index(LEVELS, a) - index(LEVELS, b);
|
||||
})
|
||||
.reduce(
|
||||
(obj, key) => {
|
||||
obj[key] = value[key];
|
||||
return obj;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
);
|
||||
}
|
||||
|
||||
export function index(based: string[], value: string) {
|
||||
const index = based.indexOf(value);
|
||||
|
||||
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
|
||||
}
|
||||
|
||||
export function levelOrLower(level: typeof LEVELS[number]) {
|
||||
const levels = [];
|
||||
for (const currentLevel of LEVELS) {
|
||||
levels.push(currentLevel);
|
||||
if (currentLevel === level) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return levels.reverse();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {computed, provide, ref} from "vue";
|
||||
import TaskDict from "../../../../../../src/components/no-code/components/tasks/TaskDict.vue";
|
||||
import TaskDict from "../../../../../../src/components/no-code/components/tasks/BlockDict.vue";
|
||||
import Wrapper from "../../../../../../src/components/no-code/components/tasks/Wrapper.vue";
|
||||
import {userEvent, waitFor, within, expect} from "storybook/internal/test";
|
||||
import {Meta, StoryObj} from "@storybook/vue3-vite";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import TaskObject from "../../../../../../src/components/no-code/components/tasks/TaskObject.vue";
|
||||
import TaskObject from "../../../../../../src/components/no-code/components/tasks/BlockObject.vue";
|
||||
import {computed, provide, ref} from "vue"
|
||||
import {StoryObj} from "@storybook/vue3-vite";
|
||||
import {waitFor, within, expect, fireEvent} from "storybook/test";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {describe, it, expect} from "vitest"
|
||||
import {YamlUtils as YAML_UTILS} from "@kestra-io/ui-libs";
|
||||
import FlowUtils from "../../../src/utils/flowUtils";
|
||||
import * as YAML_UTILS from "@kestra-io/ui-libs/flow-yaml-utils";
|
||||
import * as FlowUtils from "../../../src/utils/flowUtils";
|
||||
|
||||
export const flat = `
|
||||
id: flat
|
||||
@@ -64,32 +64,32 @@ tasks:
|
||||
|
||||
describe("FlowUtils", () => {
|
||||
it("extractTask from a flat flow", () => {
|
||||
let flow = YAML_UTILS.parse(flat);
|
||||
let findTaskById = FlowUtils.findTaskById(flow, "1-2");
|
||||
const flow = YAML_UTILS.parse(flat);
|
||||
const findTaskById = FlowUtils.findTaskById(flow, "1-2");
|
||||
|
||||
expect(findTaskById.id).toBe("1-2");
|
||||
expect(findTaskById.type).toBe("io.kestra.plugin.core.log.Log");
|
||||
})
|
||||
|
||||
it("extractTask from a flowable flow", () => {
|
||||
let flow = YAML_UTILS.parse(flowable);
|
||||
let findTaskById = FlowUtils.findTaskById(flow, "1-2");
|
||||
const flow = YAML_UTILS.parse(flowable);
|
||||
const findTaskById = FlowUtils.findTaskById(flow, "1-2");
|
||||
|
||||
expect(findTaskById.id).toBe("1-2");
|
||||
expect(findTaskById.type).toBe("io.kestra.plugin.core.log.Log");
|
||||
})
|
||||
|
||||
it("extractTask from a flowable flow", () => {
|
||||
let flow = YAML_UTILS.parse(plugins);
|
||||
let findTaskById = FlowUtils.findTaskById(flow, "nest-1");
|
||||
const flow = YAML_UTILS.parse(plugins);
|
||||
const findTaskById = FlowUtils.findTaskById(flow, "nest-1");
|
||||
|
||||
expect(findTaskById.id).toBe("nest-1");
|
||||
expect(findTaskById.type).toBe("io.kestra.core.tasks.unittest.Example");
|
||||
})
|
||||
|
||||
it("missing task from a flowable flow", () => {
|
||||
let flow = YAML_UTILS.parse(flowable);
|
||||
let findTaskById = FlowUtils.findTaskById(flow, "undefined");
|
||||
const flow = YAML_UTILS.parse(flowable);
|
||||
const findTaskById = FlowUtils.findTaskById(flow, "undefined");
|
||||
|
||||
expect(findTaskById).toBeUndefined();
|
||||
})
|
||||
Reference in New Issue
Block a user