Skip to content

Commit

Permalink
feat(ui): add validation on lowcode everywhere (#1139)
Browse files Browse the repository at this point in the history
- Added task validation at the creation
- Added trigger validation
- Added validation in "New Error" section
  • Loading branch information
Skraye authored Apr 11, 2023
1 parent 43ab765 commit 84b9a74
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 9 deletions.
2 changes: 1 addition & 1 deletion ui/src/components/flows/TaskEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
if (this.modelValue) {
this.taskObject = YamlUtils.parse(this.modelValue);
this.selectedTaskType = this.taskObject.type;
this.$store.dispatch("flow/validateTask", {task: this.modelValue})
this.$store.dispatch("flow/validateTask", {task: this.modelValue, section: this.section})
this.load();
}
Expand Down
23 changes: 21 additions & 2 deletions ui/src/components/graph/Topology.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import BottomLine from "../../components/layout/BottomLine.vue";
import TriggerFlow from "../../components/flows/TriggerFlow.vue";
import ValidationError from "../../components/flows/ValidationError.vue";
import SwitchView from "./SwitchView.vue";
import {cssVariable} from "../../utils/global"
import Cluster from "./nodes/Cluster.vue";
Expand Down Expand Up @@ -121,6 +122,11 @@
const updatedFromEditor = ref(false);
const timer = ref(null);
const dragging = ref(false);
const taskError = ref(store.getters["flow/taskError"])
watch(() => store.getters["flow/taskError"], async () => {
taskError.value = store.getters["flow/taskError"];
});
const flowables = () => {
Expand Down Expand Up @@ -562,6 +568,11 @@
}
const onUpdateNewTrigger = (event) => {
clearTimeout(timer.value);
timer.value = setTimeout(() => store.dispatch("flow/validateTask", {
task: event,
section: "trigger"
}), 500);
newTrigger.value = event;
}
Expand All @@ -583,6 +594,12 @@
}
const onUpdateNewError = (event) => {
clearTimeout(timer.value);
timer.value = setTimeout(() => store.dispatch("flow/validateTask", {
task: event,
section: "task"
}), 500);
newError.value = event;
}
Expand Down Expand Up @@ -869,7 +886,8 @@
/>
</el-form>
<template #footer>
<el-button :icon="ContentSave" @click="onSaveNewError()" type="primary">
<ValidationError :error="taskError" />
<el-button :icon="ContentSave" @click="onSaveNewError()" type="primary" :disabled="taskError">
{{ $t("save") }}
</el-button>
</template>
Expand All @@ -889,7 +907,8 @@
/>
</el-form>
<template #footer>
<el-button :icon="ContentSave" @click="onSaveNewTrigger()" type="primary">
<ValidationError :error="taskError" />
<el-button :icon="ContentSave" @click="onSaveNewTrigger()" type="primary" :disabled="taskError">
{{ $t("save") }}
</el-button>
</template>
Expand Down
16 changes: 14 additions & 2 deletions ui/src/components/graph/nodes/Edge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type {EdgeProps, Position} from '@vue-flow/core'
import {EdgeLabelRenderer, getSmoothStepPath, useEdge} from '@vue-flow/core'
import type {CSSProperties} from 'vue'
import {computed, getCurrentInstance, ref} from 'vue'
import {computed, getCurrentInstance, ref, watch} from 'vue'
import TaskEditor from "../../flows/TaskEditor.vue"
import Help from "vue-material-design-icons/Help.vue";
import HelpCircle from "vue-material-design-icons/HelpCircle.vue";
Expand All @@ -15,6 +15,7 @@
import yamlUtils from "../../../utils/yamlUtils.js";
import YamlUtils from "../../../utils/yamlUtils.js";
import {useStore} from "vuex";
import ValidationError from "../../flows/ValidationError.vue";
const store = useStore();
const t = getCurrentInstance().appContext.config.globalProperties.$t;
Expand All @@ -41,6 +42,12 @@
const emit = defineEmits(["edit"])
const taskYaml = ref("");
const execution = store.getters["execution/execution"];
const timer = ref(undefined);
const taskError = ref(store.getters["flow/taskError"])
watch(() => store.getters["flow/taskError"], async () => {
taskError.value = store.getters["flow/taskError"];
});
const isBorderEdge = () => {
if (!props.data.haveAdd && props.data.isFlowable) {
Expand Down Expand Up @@ -104,6 +111,10 @@
const updateTask = (task) => {
taskYaml.value = task;
clearTimeout(timer.value);
timer.value = setTimeout(() => {
store.dispatch("flow/validateTask", {task: task})
}, 500);
}
const getAddTaskInformation = () => {
Expand Down Expand Up @@ -237,7 +248,8 @@
/>
</el-form>
<template #footer>
<el-button :disabled="!taskHaveId()" :icon="ContentSave" @click="forwardTask" type="primary">
<ValidationError link :error="taskError"/>
<el-button :disabled="!taskHaveId() || taskError" :icon="ContentSave" @click="forwardTask" type="primary">
{{ $t("save") }}
</el-button>
</template>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/stores/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export default {
})
},
validateTask({commit}, options) {
return axios.post(`${apiRoot}flows/validate/task`, options.task, textYamlHeader)
return axios.post(`${apiRoot}flows/validate/task`, options.task, {...textYamlHeader, params: {section: options.section ? options.section : "task"}})
.then(response => {
commit("setTaskError", response.data.constraints)
return response.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.kestra.core.models.tasks.Task;
import io.kestra.core.models.topologies.FlowTopology;
import io.kestra.core.models.topologies.FlowTopologyGraph;
import io.kestra.core.models.triggers.AbstractTrigger;
import io.kestra.core.models.validations.ManualConstraintViolation;
import io.kestra.core.models.validations.ModelValidator;
import io.kestra.core.models.validations.ValidateConstraintViolation;
Expand Down Expand Up @@ -470,13 +471,21 @@ public List<ValidateConstraintViolation> validateFlows(
@Post(uri = "/validate/task", produces = MediaType.TEXT_JSON, consumes = MediaType.APPLICATION_YAML)
@Operation(tags = {"Flows"}, summary = "Validate a list of flows")
public ValidateConstraintViolation validateTask(
@Parameter(description = "A list of flows") @Body String task
@Parameter(description = "A list of flows") @Body String task,
@Parameter(description = "Type of task") @QueryValue(defaultValue = "task") String section
) {
ValidateConstraintViolation.ValidateConstraintViolationBuilder<?, ?> validateConstraintViolationBuilder = ValidateConstraintViolation.builder();

try {
Task taskParse = yamlFlowParser.parse(task, Task.class);
modelValidator.validate(taskParse);
if (section.equals("task")) {
Task taskParse = yamlFlowParser.parse(task, Task.class);
modelValidator.validate(taskParse);
} else if (section.equals("trigger")) {
AbstractTrigger triggerParse = yamlFlowParser.parse(task, AbstractTrigger.class);
modelValidator.validate(triggerParse);
} else {
throw new IllegalArgumentException("Invalid section, must be 'task' or 'trigger'");
}
} catch (ConstraintViolationException e) {
validateConstraintViolationBuilder.constraints(e.getMessage());
}
Expand Down

0 comments on commit 84b9a74

Please sign in to comment.