[Improvement][UI] Support to view the process variables on the page of DAG. (#12609) (#12616)

Co-authored-by: calvin <jianghuachinacom@163.com>
This commit is contained in:
Kerwin 2022-10-31 12:22:29 +08:00 committed by GitHub
parent 5eaf105290
commit 64360e5b69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 223 additions and 38 deletions

View File

@ -27,6 +27,7 @@ import static org.apache.dolphinscheduler.api.enums.Status.ENCAPSULATION_TREEVIE
import static org.apache.dolphinscheduler.api.enums.Status.GET_TASKS_LIST_BY_PROCESS_DEFINITION_ID_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.GET_TASKS_LIST_BY_PROCESS_DEFINITION_ID_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.IMPORT_PROCESS_DEFINE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.IMPORT_PROCESS_DEFINE_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_DETAIL_OF_PROCESS_DEFINITION_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_DETAIL_OF_PROCESS_DEFINITION_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_ALL_VARIABLES_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_LIST; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_LIST;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_LIST_PAGING_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_LIST_PAGING_ERROR;
import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_VERSIONS_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_PROCESS_DEFINITION_VERSIONS_ERROR;
@ -903,4 +904,27 @@ public class ProcessDefinitionController extends BaseController {
return returnDataList( return returnDataList(
processDefinitionService.releaseWorkflowAndSchedule(loginUser, projectCode, code, releaseState)); processDefinitionService.releaseWorkflowAndSchedule(loginUser, projectCode, code, releaseState));
} }
/**
* query process definition global variables and local variables
*
* @param loginUser login user
* @param code process definition code
* @return variables data
*/
@Operation(summary = "viewVariables", description = "QUERY_PROCESS_DEFINITION_GLOBAL_VARIABLES_AND_LOCAL_VARIABLES_NOTES")
@Parameters({
@Parameter(name = "code", description = "PROCESS_DEFINITION_CODE", required = true, schema = @Schema(implementation = long.class, example = "100"))
})
@GetMapping(value = "/{code}/view-variables")
@ResponseStatus(HttpStatus.OK)
@ApiException(QUERY_PROCESS_DEFINITION_ALL_VARIABLES_ERROR)
@AccessLogAnnotation
public Result viewVariables(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@Parameter(name = "projectCode", description = "PROJECT_CODE", required = true) @PathVariable long projectCode,
@PathVariable("code") Long code) {
Map<String, Object> result = processDefinitionService.viewVariables(loginUser, projectCode, code);
return returnDataList(result);
}
} }

View File

@ -436,6 +436,9 @@ public enum Status {
USER_PASSWORD_LENGTH_ERROR(1300017, "user's password length error", "用户密码长度错误"), USER_PASSWORD_LENGTH_ERROR(1300017, "user's password length error", "用户密码长度错误"),
QUERY_CAN_USE_K8S_NAMESPACE_ERROR(1300018, "login user query can used namespace list error", "查询可用命名空间错误"), QUERY_CAN_USE_K8S_NAMESPACE_ERROR(1300018, "login user query can used namespace list error", "查询可用命名空间错误"),
QUERY_PROCESS_DEFINITION_ALL_VARIABLES_ERROR(1300100, "query process definition all variables error",
"查询工作流自定义变量信息错误"),
NO_CURRENT_OPERATING_PERMISSION(1400001, "The current user does not have this permission.", "当前用户无此权限"), NO_CURRENT_OPERATING_PERMISSION(1400001, "The current user does not have this permission.", "当前用户无此权限"),
FUNCTION_DISABLED(1400002, "The current feature is disabled.", "当前功能已被禁用"), FUNCTION_DISABLED(1400002, "The current feature is disabled.", "当前功能已被禁用"),
SCHEDULE_TIME_NUMBER(1400003, "The number of complement dates exceed 100.", "补数日期个数超过100"), SCHEDULE_TIME_NUMBER(1400003, "The number of complement dates exceed 100.", "补数日期个数超过100"),

View File

@ -469,4 +469,13 @@ public interface ProcessDefinitionService {
* @return Json String * @return Json String
*/ */
String doOtherOperateProcess(User loginUser, ProcessDefinition processDefinition); String doOtherOperateProcess(User loginUser, ProcessDefinition processDefinition);
/**
* view process variables
* @param loginUser login user
* @param projectCode project code
* @param code process definition code
* @return variables data
*/
Map<String, Object> viewVariables(User loginUser, long projectCode, long code);
} }

View File

@ -31,11 +31,17 @@ import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationCon
import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_SWITCH_TO_THIS_VERSION; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_SWITCH_TO_THIS_VERSION;
import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_TREE_VIEW; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_TREE_VIEW;
import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_UPDATE; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_UPDATE;
import static org.apache.dolphinscheduler.api.enums.Status.PROCESS_DEFINE_NOT_EXIST;
import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE; import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE;
import static org.apache.dolphinscheduler.common.constants.Constants.COPY_SUFFIX; import static org.apache.dolphinscheduler.common.constants.Constants.COPY_SUFFIX;
import static org.apache.dolphinscheduler.common.constants.Constants.DATA_LIST;
import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP; import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP;
import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING; import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING;
import static org.apache.dolphinscheduler.common.constants.Constants.GLOBAL_PARAMS;
import static org.apache.dolphinscheduler.common.constants.Constants.IMPORT_SUFFIX; import static org.apache.dolphinscheduler.common.constants.Constants.IMPORT_SUFFIX;
import static org.apache.dolphinscheduler.common.constants.Constants.LOCAL_PARAMS;
import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.LOCAL_PARAMS_LIST;
import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE;
import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SQL; import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SQL;
import org.apache.dolphinscheduler.api.dto.DagDataSchedule; import org.apache.dolphinscheduler.api.dto.DagDataSchedule;
@ -102,6 +108,7 @@ import org.apache.dolphinscheduler.dao.model.PageListingResult;
import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionDao; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionDao;
import org.apache.dolphinscheduler.plugin.task.api.enums.SqlType; import org.apache.dolphinscheduler.plugin.task.api.enums.SqlType;
import org.apache.dolphinscheduler.plugin.task.api.enums.TaskTimeoutStrategy; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskTimeoutStrategy;
import org.apache.dolphinscheduler.plugin.task.api.model.Property;
import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode;
import org.apache.dolphinscheduler.plugin.task.api.parameters.SqlParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.SqlParameters;
import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.model.TaskNode;
@ -128,6 +135,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -2594,6 +2602,89 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
return null; return null;
} }
/**
* view process variables
* @param loginUser login user
* @param projectCode project code
* @param code process definition code
* @return variables data
*/
@Override
public Map<String, Object> viewVariables(User loginUser, long projectCode, long code) {
Project project = projectMapper.queryByCode(projectCode);
// check user access for project
Map<String, Object> result =
projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_DEFINITION);
if (result.get(Constants.STATUS) != Status.SUCCESS) {
return result;
}
ProcessDefinition processDefinition = processDefinitionMapper.queryByCode(code);
if (Objects.isNull(processDefinition) || projectCode != processDefinition.getProjectCode()) {
logger.error("Process definition does not exist, projectCode:{}, processDefinitionCode:{}.", projectCode,
code);
putMsg(result, PROCESS_DEFINE_NOT_EXIST, code);
return result;
}
// global params
List<Property> globalParams = processDefinition.getGlobalParamList();
Map<String, Map<String, Object>> localUserDefParams = getLocalParams(processDefinition);
Map<String, Object> resultMap = new HashMap<>();
if (Objects.nonNull(globalParams)) {
resultMap.put(GLOBAL_PARAMS, globalParams);
}
if (Objects.nonNull(localUserDefParams)) {
resultMap.put(LOCAL_PARAMS, localUserDefParams);
}
result.put(DATA_LIST, resultMap);
putMsg(result, Status.SUCCESS);
return result;
}
/**
* get local params
*/
private Map<String, Map<String, Object>> getLocalParams(ProcessDefinition processDefinition) {
Map<String, Map<String, Object>> localUserDefParams = new HashMap<>();
Set<Long> taskCodeSet = new TreeSet<>();
processTaskRelationMapper.queryByProcessCode(processDefinition.getProjectCode(), processDefinition.getCode())
.forEach(processTaskRelation -> {
if (processTaskRelation.getPreTaskCode() > 0) {
taskCodeSet.add(processTaskRelation.getPreTaskCode());
}
if (processTaskRelation.getPostTaskCode() > 0) {
taskCodeSet.add(processTaskRelation.getPostTaskCode());
}
});
taskDefinitionMapper.queryByCodeList(taskCodeSet)
.stream().forEach(taskDefinition -> {
Map<String, Object> localParamsMap = new HashMap<>();
String localParams = JSONUtils.getNodeString(taskDefinition.getTaskParams(), LOCAL_PARAMS);
if (!StringUtils.isEmpty(localParams)) {
List<Property> localParamsList = JSONUtils.toList(localParams, Property.class);
localParamsMap.put(TASK_TYPE, taskDefinition.getTaskType());
localParamsMap.put(LOCAL_PARAMS_LIST, localParamsList);
if (CollectionUtils.isNotEmpty(localParamsList)) {
localUserDefParams.put(taskDefinition.getName(), localParamsMap);
}
}
});
return localUserDefParams;
}
/** /**
* delete other relation * delete other relation
* @param project * @param project

View File

@ -437,4 +437,18 @@ public class ProcessDefinitionControllerTest {
Assert.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode()); Assert.assertEquals(Status.SUCCESS.getCode(), (int) result.getCode());
} }
@Test
public void testViewVariables() {
long projectCode = 1L;
Map<String, Object> resultMap = new HashMap<>();
putMsg(resultMap, Status.SUCCESS);
Mockito.when(processDefinitionService.viewVariables(user, projectCode, 1))
.thenReturn(resultMap);
Result result = processDefinitionController.viewVariables(user, projectCode, 1L);
Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue());
}
} }

View File

@ -859,6 +859,28 @@ public class ProcessDefinitionServiceTest {
Assert.assertEquals(2, newName3.split(Constants.IMPORT_SUFFIX).length); Assert.assertEquals(2, newName3.split(Constants.IMPORT_SUFFIX).length);
} }
@Test
public void testViewVariables() {
Mockito.when(projectMapper.queryByCode(projectCode)).thenReturn(getProject(projectCode));
Project project = getProject(projectCode);
ProcessDefinition processDefinition = getProcessDefinition();
Map<String, Object> result = new HashMap<>();
putMsg(result, Status.PROJECT_NOT_FOUND, projectCode);
// project check auth fail
Mockito.when(projectService.checkProjectAndAuth(user, project, projectCode, WORKFLOW_DEFINITION))
.thenReturn(result);
Map<String, Object> map =
processDefinitionService.viewVariables(user, processDefinition.getProjectCode(),
processDefinition.getCode());
Assertions.assertEquals(Status.PROJECT_NOT_FOUND, map.get(Constants.STATUS));
}
/** /**
* get mock processDefinition * get mock processDefinition
* *

View File

@ -247,3 +247,11 @@ export function viewTree(
params params
}) })
} }
export function viewProcessDefinitionVariables(code: number, processCode: number): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/view-variables`,
method: 'get'
})
}

View File

@ -130,3 +130,4 @@ export function viewVariables(id: number, code: number): any {
method: 'get' method: 'get'
}) })
} }

View File

@ -215,6 +215,41 @@ export default defineComponent({
}} }}
></NTooltip> ></NTooltip>
)} )}
{props.definition?.processDefinition?.name && (
<NTooltip
v-slots={{
trigger: () => (
<NPopover
placement='bottom'
trigger='click'
scrollable
style={{ maxWidth: '50vw', maxHeight: '70vh' }}
>
{{
trigger: () => (
<NButton
quaternary
circle
class={Styles['toolbar-btn']}
>
<NIcon>
<FundViewOutlined />
</NIcon>
</NButton>
),
header: () => (
<NText strong depth={1}>
{t('project.workflow.parameters_variables')}
</NText>
),
default: () => <VariablesView onCopy={copy} />
}}
</NPopover>
),
default: () => t('project.dag.view_variables')
}}
></NTooltip>
)}
<div class={Styles['toolbar-left-part']}> <div class={Styles['toolbar-left-part']}>
{route.name !== 'workflow-instance-detail' && {route.name !== 'workflow-instance-detail' &&
props.definition?.processDefinition?.releaseState === 'ONLINE' && ( props.definition?.processDefinition?.releaseState === 'ONLINE' && (
@ -224,39 +259,6 @@ export default defineComponent({
)} )}
{route.name === 'workflow-instance-detail' && ( {route.name === 'workflow-instance-detail' && (
<> <>
<NTooltip
v-slots={{
trigger: () => (
<NPopover
placement='bottom'
trigger='click'
scrollable
style={{ maxWidth: '50vw', maxHeight: '70vh' }}
>
{{
trigger: () => (
<NButton
quaternary
circle
class={Styles['toolbar-btn']}
>
<NIcon>
<FundViewOutlined />
</NIcon>
</NButton>
),
header: () => (
<NText strong depth={1}>
{t('project.workflow.parameters_variables')}
</NText>
),
default: () => <VariablesView onCopy={copy} />
}}
</NPopover>
),
default: () => t('project.dag.view_variables')
}}
></NTooltip>
<NTooltip <NTooltip
v-slots={{ v-slots={{
trigger: () => ( trigger: () => (

View File

@ -19,9 +19,11 @@ import { useRoute } from 'vue-router'
import { defineComponent, onMounted, ref, computed } from 'vue' import { defineComponent, onMounted, ref, computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { viewVariables } from '@/service/modules/process-instances' import { viewVariables } from '@/service/modules/process-instances'
import { viewProcessDefinitionVariables } from "@/service/modules/process-definition";
import styles from './variables.module.scss' import styles from './variables.module.scss'
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
export default defineComponent({ export default defineComponent({
name: 'variables-view', name: 'variables-view',
emits: ['copy'], emits: ['copy'],
@ -30,20 +32,29 @@ export default defineComponent({
const route = useRoute() const route = useRoute()
const projectCode = Number(route.params.projectCode) const projectCode = Number(route.params.projectCode)
const instanceId = Number(route.params.id) const instanceId = Number(route.params.id)
const processCode = Number(route.params.code)
const globalParams = computed(() => { const globalParams = computed(() => {
return paramsRef.value ? paramsRef.value.globalParams : [] return paramsRef.value && paramsRef.value.globalParams ? paramsRef.value.globalParams : []
}) })
const localParams = computed(() => { const localParams = computed(() => {
return paramsRef.value ? paramsRef.value.localParams : {} return paramsRef.value && paramsRef.value.localParams ? paramsRef.value.localParams : {}
}) })
const getViewVariables = () => { const getViewVariables = () => {
viewVariables(instanceId, projectCode).then((res: any) => { if (Number.isNaN(instanceId)) {
paramsRef.value = res viewProcessDefinitionVariables(projectCode, processCode).then((res: any) => {
}) paramsRef.value = res
})
} else {
viewVariables(instanceId, projectCode).then((res: any) => {
paramsRef.value = res
})
}
} }
const handleCopy = (text: string) => { const handleCopy = (text: string) => {