mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-19 18:05:41 -05:00
fix: avoid blocking creation of flow when edition is restricted to a namespace (#13694)
This commit is contained in:
committed by
GitHub
parent
67ada7f61b
commit
abcf76f7b4
@@ -20,6 +20,9 @@
|
||||
import {useVueTour} from "../../composables/useVueTour";
|
||||
|
||||
import type {BlueprintType} from "../../stores/blueprints"
|
||||
import {useAuthStore} from "../../override/stores/auth";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
|
||||
const route = useRoute();
|
||||
const {t} = useI18n();
|
||||
@@ -29,13 +32,21 @@
|
||||
const blueprintsStore = useBlueprintsStore();
|
||||
const coreStore = useCoreStore();
|
||||
const flowStore = useFlowStore();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const setupFlow = async () => {
|
||||
const blueprintId = route.query.blueprintId as string;
|
||||
const blueprintSource = route.query.blueprintSource as BlueprintType;
|
||||
const implicitDefaultNamespace = authStore.user.getNamespacesForAction(
|
||||
permission.FLOW,
|
||||
action.CREATE,
|
||||
)[0];
|
||||
let flowYaml = "";
|
||||
const id = getRandomID();
|
||||
const selectedNamespace = (route.query.namespace as string) || defaultNamespace() || "company.team";
|
||||
const selectedNamespace = (route.query.namespace as string)
|
||||
?? defaultNamespace()
|
||||
?? implicitDefaultNamespace
|
||||
?? "company.team";
|
||||
|
||||
if (route.query.copy && flowStore.flow) {
|
||||
flowYaml = flowStore.flow.source;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<span ref="rootContainer">
|
||||
<!-- Valid -->
|
||||
<el-button v-if="!errors && !warnings &&!infos" v-bind="$attrs" :link="link" :size="size" type="default" class="success square" disabled>
|
||||
<el-button v-if="!errors && !warnings && !infos" v-bind="$attrs" :link="link" :size="size" type="default" class="success square" disabled>
|
||||
<CheckBoldIcon class="text-success" />
|
||||
</el-button>
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
}
|
||||
|
||||
&.success {
|
||||
cursor: default;
|
||||
border-color: var(--ks-border-success);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
<ValidationError
|
||||
class="validation"
|
||||
tooltipPlacement="bottom-start"
|
||||
:errors="flowErrors"
|
||||
:errors="flowStore.flowErrors"
|
||||
:warnings="flowWarnings"
|
||||
:infos="flowInfos"
|
||||
:infos="flowStore.flowInfos"
|
||||
/>
|
||||
|
||||
<EditorButtons
|
||||
:isCreating="flowStore.isCreating"
|
||||
:isReadOnly="isReadOnly"
|
||||
:isReadOnly="flowStore.isReadOnly"
|
||||
:canDelete="true"
|
||||
:isAllowedEdit="isAllowedEdit"
|
||||
:isAllowedEdit="flowStore.isAllowedEdit"
|
||||
:haveChange="haveChange"
|
||||
:flowHaveTasks="Boolean(flowHaveTasks)"
|
||||
:errors="flowErrors"
|
||||
:flowHaveTasks="Boolean(flowStore.flowHaveTasks)"
|
||||
:errors="flowStore.flowErrors"
|
||||
:warnings="flowWarnings"
|
||||
@save="save"
|
||||
@copy="
|
||||
@@ -49,7 +49,6 @@
|
||||
import ValidationError from "../flows/ValidationError.vue";
|
||||
|
||||
import localUtils from "../../utils/utils";
|
||||
import {useFlowOutdatedErrors} from "./flowOutdatedErrors";
|
||||
import {useFlowStore} from "../../stores/flow";
|
||||
import {useToast} from "../../utils/toast";
|
||||
|
||||
@@ -73,22 +72,14 @@
|
||||
const route = useRoute()
|
||||
const routeParams = computed(() => route.params)
|
||||
|
||||
const {translateError, translateErrorWithKey} = useFlowOutdatedErrors();
|
||||
|
||||
// If playground is not defined, enable it by default
|
||||
const isSettingsPlaygroundEnabled = computed(() => localStorage.getItem("editorPlayground") === "false" ? false : true);
|
||||
|
||||
const isReadOnly = computed(() => flowStore.isReadOnly)
|
||||
const isAllowedEdit = computed(() => flowStore.isAllowedEdit)
|
||||
const flowHaveTasks = computed(() => flowStore.flowHaveTasks)
|
||||
const flowErrors = computed(() => flowStore.flowErrors?.map(translateError));
|
||||
const flowInfos = computed(() => flowStore.flowInfos)
|
||||
const toast = useToast();
|
||||
const flowWarnings = computed(() => {
|
||||
|
||||
const outdatedWarning =
|
||||
flowStore.flowValidation?.outdated && !flowStore.isCreating
|
||||
? [translateErrorWithKey(flowStore.flowValidation?.constraints ?? "")]
|
||||
? flowStore.flowValidation?.constraints?.split(", ") ?? []
|
||||
: [];
|
||||
|
||||
const deprecationWarnings =
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import {useI18n} from "vue-i18n";
|
||||
|
||||
export function useFlowOutdatedErrors(){
|
||||
const {t} = useI18n();
|
||||
function translateError(error: string): string {
|
||||
if(error.startsWith(">>>>")){
|
||||
const key = error.substring(4).trim();
|
||||
return translateErrorWithKey(key);
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
function translateErrorWithKey(key: string): string {
|
||||
return `${t(key + ".description")} ${t(key + ".details")}`
|
||||
}
|
||||
|
||||
return {
|
||||
translateError,
|
||||
translateErrorWithKey
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,10 @@ export class Me {
|
||||
hasAnyRole() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getNamespacesForAction(_permission: any, _action: any): string[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore("auth", {
|
||||
|
||||
@@ -195,7 +195,7 @@ export const useFlowStore = defineStore("flow", () => {
|
||||
return validateFlow({
|
||||
flow: (isCreating.value ? flowYaml.value : yamlWithNextRevision.value) ?? ""
|
||||
})
|
||||
.then((value: {constraints?: any}) => {
|
||||
.then((value: {constraints?: string}) => {
|
||||
if (
|
||||
topologyVisible &&
|
||||
flowHaveTasks.value &&
|
||||
@@ -566,7 +566,7 @@ function deleteFlowAndDependencies() {
|
||||
coreStore.message = {
|
||||
title: "Couldn't expand subflow",
|
||||
message: error.response.data.message,
|
||||
variant: "danger"
|
||||
variant: "error"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -644,19 +644,37 @@ function deleteFlowAndDependencies() {
|
||||
function enableFlowByQuery(options: { namespace: string, id: string }) {
|
||||
return axios.post(`${apiUrl()}/flows/enable/by-query`, options, {params: options})
|
||||
}
|
||||
|
||||
function deleteFlowByIds(options: { ids: {id: string, namespace: string}[] }) {
|
||||
return axios.delete(`${apiUrl()}/flows/delete/by-ids`, {data: options.ids})
|
||||
}
|
||||
|
||||
function deleteFlowByQuery(options: { namespace: string, id: string }) {
|
||||
return axios.delete(`${apiUrl()}/flows/delete/by-query`, {params: options})
|
||||
}
|
||||
|
||||
function validateFlow(options: { flow: string }) {
|
||||
const flowValidationIssues: FlowValidations = {};
|
||||
if(isCreating.value) {
|
||||
const {namespace} = YAML_UTILS.getMetadata(options.flow);
|
||||
if(authStore.user && !authStore.user.isAllowed(
|
||||
permission.FLOW,
|
||||
action.CREATE,
|
||||
namespace,
|
||||
)) {
|
||||
flowValidationIssues.constraints = t("flow creation denied in namespace", {namespace});
|
||||
}
|
||||
}
|
||||
return axios.post(`${apiUrl()}/flows/validate`, options.flow, {...textYamlHeader, withCredentials: true})
|
||||
.then(response => {
|
||||
flowValidation.value = response.data[0]
|
||||
return response.data[0]
|
||||
const constraintsArray = [response?.data[0]?.constraints, flowValidationIssues.constraints].filter(Boolean)
|
||||
flowValidation.value = constraintsArray.length === 0 ? {} : {
|
||||
constraints: constraintsArray.join(", ")
|
||||
};
|
||||
return flowValidation.value
|
||||
})
|
||||
}
|
||||
|
||||
function validateTask(options: { task: string, section: string }) {
|
||||
return axios.post(`${apiUrl()}/flows/validate/task`, options.task, {...textYamlHeader, withCredentials: true, params: {section: options.section}})
|
||||
.then(response => {
|
||||
@@ -752,7 +770,8 @@ function deleteFlowAndDependencies() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return authStore.user.isAllowed(
|
||||
return (isCreating.value && authStore.user.hasAnyAction(permission.FLOW, action.UPDATE))
|
||||
|| authStore.user.isAllowed(
|
||||
permission.FLOW,
|
||||
action.UPDATE,
|
||||
flow.value?.namespace,
|
||||
@@ -777,9 +796,10 @@ function deleteFlowAndDependencies() {
|
||||
})
|
||||
|
||||
const flowErrors = computed((): string[] | undefined => {
|
||||
const key = baseOutdatedTranslationKey.value;
|
||||
const flowExistsError =
|
||||
flowValidation.value?.outdated && isCreating.value
|
||||
? [`>>>>${baseOutdatedTranslationKey.value}`] // because translating is impossible here
|
||||
? [`${t(key + ".description")} ${t(key + ".details")}`]
|
||||
: [];
|
||||
|
||||
const constraintsError =
|
||||
@@ -794,8 +814,6 @@ function deleteFlowAndDependencies() {
|
||||
const infos = flowValidation.value?.infos ?? [];
|
||||
|
||||
return infos.length === 0 ? undefined : infos;
|
||||
|
||||
return undefined;
|
||||
})
|
||||
|
||||
const flowHaveTasks = computed((): boolean => {
|
||||
|
||||
@@ -574,6 +574,7 @@
|
||||
"can not save": "Can not save",
|
||||
"flow must not be empty": "Flow must not be empty",
|
||||
"flow must have id and namespace": "Flow must have an id and a namespace.",
|
||||
"flow creation denied in namespace": "You don't have permission to create flows in the namespace `{namespace}`.",
|
||||
"readonly property": "Read-only property",
|
||||
"namespace and id readonly": "The properties `namespace` and `id` cannot be changed — they are now set to their initial values. If you want to rename a flow or change its namespace, you can create a new flow and remove the old one.",
|
||||
"avg": "Average",
|
||||
|
||||
Reference in New Issue
Block a user