未验证 提交 e7e5bf3a 编写于 作者: J JiPeng Wang 提交者: GitHub

Merge pull request #79 from WhaleOps/process_name_check

process name verify fix
......@@ -195,7 +195,9 @@ public class ProcessDefinitionController extends BaseController {
*/
@ApiOperation(value = "verify-name", notes = "VERIFY_PROCESS_DEFINITION_NAME_NOTES")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String")
@ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, type = "String"),
@ApiImplicitParam(name = "name", value = "PROCESS_DEFINITION_NAME", required = true, dataTypeClass = String.class),
@ApiImplicitParam(name = "code", value = "PROCESS_DEFINITION_CODE", required = false, dataTypeClass = Long.class)
})
@GetMapping(value = "/verify-name")
@ResponseStatus(HttpStatus.OK)
......@@ -203,8 +205,9 @@ public class ProcessDefinitionController extends BaseController {
@AccessLogAnnotation(ignoreRequestArgs = "loginUser")
public Result verifyProcessDefinitionName(@ApiIgnore @RequestAttribute(value = Constants.SESSION_USER) User loginUser,
@ApiParam(name = "projectCode", value = "PROJECT_CODE", required = true) @PathVariable long projectCode,
@RequestParam(value = "name", required = true) String name) {
Map<String, Object> result = processDefinitionService.verifyProcessDefinitionName(loginUser, projectCode, name);
@RequestParam(value = "name", required = true) String name,
@RequestParam(value = "code", required = false, defaultValue = "0") long processDefinitionCode) {
Map<String, Object> result = processDefinitionService.verifyProcessDefinitionName(loginUser, projectCode, name, processDefinitionCode);
return returnDataList(result);
}
......
......@@ -259,7 +259,7 @@ public class PythonGateway {
* @param processDefinitionName process definition name
*/
private ProcessDefinition getProcessDefinition(User user, long projectCode, String processDefinitionName) {
Map<String, Object> verifyProcessDefinitionExists = processDefinitionService.verifyProcessDefinitionName(user, projectCode, processDefinitionName);
Map<String, Object> verifyProcessDefinitionExists = processDefinitionService.verifyProcessDefinitionName(user, projectCode, processDefinitionName, 0);
Status verifyStatus = (Status) verifyProcessDefinitionExists.get(Constants.STATUS);
ProcessDefinition processDefinition = null;
......
......@@ -195,11 +195,13 @@ public interface ProcessDefinitionService {
* @param loginUser login user
* @param projectCode project code
* @param name name
* @param processDefinitionCode processDefinitionCode
* @return true if process definition name not exists, otherwise false
*/
Map<String, Object> verifyProcessDefinitionName(User loginUser,
long projectCode,
String name);
String name,
long processDefinitionCode);
/**
* delete process definition by code
......
......@@ -724,7 +724,7 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
* @return true if process definition name not exists, otherwise false
*/
@Override
public Map<String, Object> verifyProcessDefinitionName(User loginUser, long projectCode, String name) {
public Map<String, Object> verifyProcessDefinitionName(User loginUser, long projectCode, String name, long processDefinitionCode) {
Project project = projectMapper.queryByCode(projectCode);
//check user access for project
Map<String, Object> result = projectService.checkProjectAndAuth(loginUser, project, projectCode,WORKFLOW_CREATE);
......@@ -734,9 +734,13 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
ProcessDefinition processDefinition = processDefinitionMapper.verifyByDefineName(project.getCode(), name.trim());
if (processDefinition == null) {
putMsg(result, Status.SUCCESS);
} else {
putMsg(result, Status.PROCESS_DEFINITION_NAME_EXIST, name.trim());
return result;
}
if (processDefinitionCode != 0 && processDefinitionCode == processDefinition.getCode()) {
putMsg(result, Status.SUCCESS);
return result;
}
putMsg(result, Status.PROCESS_DEFINITION_NAME_EXIST, name.trim());
return result;
}
......@@ -1215,7 +1219,7 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro
String importProcessDefinitionName = processDefinitionName + "_import_" + DateUtils.getCurrentTimeStamp();
//unique check
Map<String, Object> checkResult = verifyProcessDefinitionName(loginUser, projectCode, importProcessDefinitionName);
Map<String, Object> checkResult = verifyProcessDefinitionName(loginUser, projectCode, importProcessDefinitionName, 0);
if (Status.SUCCESS.equals(checkResult.get(Constants.STATUS))) {
putMsg(result, Status.SUCCESS);
} else {
......
......@@ -125,9 +125,9 @@ public class ProcessDefinitionControllerTest {
long projectCode = 1L;
String name = "dag_test";
Mockito.when(processDefinitionService.verifyProcessDefinitionName(user, projectCode, name)).thenReturn(result);
Mockito.when(processDefinitionService.verifyProcessDefinitionName(user, projectCode, name, 0)).thenReturn(result);
Result response = processDefinitionController.verifyProcessDefinitionName(user, projectCode, name);
Result response = processDefinitionController.verifyProcessDefinitionName(user, projectCode, name, 0);
Assert.assertTrue(response.isStatus(Status.PROCESS_DEFINITION_NAME_EXIST));
}
......
......@@ -507,20 +507,20 @@ public class ProcessDefinitionServiceTest {
putMsg(result, Status.PROJECT_NOT_FOUND, projectCode);
Mockito.when(projectService.checkProjectAndAuth(loginUser, project, projectCode, WORKFLOW_CREATE)).thenReturn(result);
Map<String, Object> map = processDefinitionService.verifyProcessDefinitionName(loginUser,
projectCode, "test_pdf");
projectCode, "test_pdf", 0);
Assert.assertEquals(Status.PROJECT_NOT_FOUND, map.get(Constants.STATUS));
//project check auth success, process not exist
putMsg(result, Status.SUCCESS, projectCode);
Mockito.when(processDefineMapper.verifyByDefineName(project.getCode(), "test_pdf")).thenReturn(null);
Map<String, Object> processNotExistRes = processDefinitionService.verifyProcessDefinitionName(loginUser,
projectCode, "test_pdf");
projectCode, "test_pdf", 0);
Assert.assertEquals(Status.SUCCESS, processNotExistRes.get(Constants.STATUS));
//process exist
Mockito.when(processDefineMapper.verifyByDefineName(project.getCode(), "test_pdf")).thenReturn(getProcessDefinition());
Map<String, Object> processExistRes = processDefinitionService.verifyProcessDefinitionName(loginUser,
projectCode, "test_pdf");
projectCode, "test_pdf", 0);
Assert.assertEquals(Status.PROCESS_DEFINITION_NAME_EXIST, processExistRes.get(Constants.STATUS));
}
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { axios } from '@/service/service'
import {
CodeReq,
CodesReq,
NameReq,
ReleaseStateReq,
LimitReq,
PageReq,
ListReq,
ProcessDefinitionReq,
TargetCodeReq
} from './types'
export function queryListPaging(params: PageReq & ListReq, code: number): any {
return axios({
url: `/projects/${code}/process-definition`,
method: 'get',
params
})
}
export function createProcessDefinition(
data: ProcessDefinitionReq,
projectCode: number
): any {
return axios({
url: `/projects/${projectCode}/process-definition`,
method: 'post',
data
})
}
export function queryProcessDefinitionList(projectCode: number): any {
return axios({
url: `/projects/${projectCode}/process-definition/query-process-definition-list`,
method: 'get'
})
}
export function batchCopyByCodes(
data: TargetCodeReq & CodesReq,
code: number
): any {
return axios({
url: `/projects/${code}/process-definition/batch-copy`,
method: 'post',
data
})
}
export function batchDeleteByCodes(data: CodesReq, code: number): any {
return axios({
url: `/projects/${code}/process-definition/batch-delete`,
method: 'post',
data
})
}
export function batchExportByCodes(data: CodesReq, code: number): any {
return axios({
url: `/projects/${code}/process-definition/batch-export`,
method: 'post',
responseType: 'blob',
data
})
}
export function batchMoveByCodes(
data: TargetCodeReq & CodesReq,
code: CodeReq
): any {
return axios({
url: `/projects/${code}/process-definition/batch-move`,
method: 'post',
data
})
}
export function getTaskListByDefinitionCodes(
params: CodesReq,
code: number
): any {
return axios({
url: `/projects/${code}/process-definition/batch-query-tasks`,
method: 'get',
params
})
}
export function importProcessDefinition(data: FormData, code: number): any {
return axios({
url: `/projects/${code}/process-definition/import`,
method: 'post',
data
})
}
export function queryList(code: CodeReq): any {
return axios({
url: `/projects/${code}/process-definition/list`,
method: 'get'
})
}
export function queryProcessDefinitionByName(
params: NameReq,
code: CodeReq
): any {
return axios({
url: `/projects/${code}/process-definition/query-by-name`,
method: 'get',
params
})
}
export function querySimpleList(code: number): any {
return axios({
url: `/projects/${code}/process-definition/simple-list`,
method: 'get'
})
}
export function verifyName(
params: { name: string; workflowCode?: number },
projectCode: number
): any {
return axios({
url: `/projects/${projectCode}/process-definition/verify-name`,
method: 'get',
params
})
}
export function queryProcessDefinitionByCode(
code: number,
projectCode: number
): any {
return axios({
url: `/projects/${projectCode}/process-definition/${code}`,
method: 'get'
})
}
export function updateProcessDefinition(
data: ProcessDefinitionReq & ReleaseStateReq,
code: number,
projectCode: number
): any {
return axios({
url: `/projects/${projectCode}/process-definition/${code}`,
method: 'put',
data
})
}
export function deleteByCode(code: number, processCode: number): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}`,
method: 'delete'
})
}
export function release(
data: NameReq & ReleaseStateReq,
code: number,
processCode: number
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/release`,
method: 'post',
data
})
}
export function getTasksByDefinitionList(
projectCode: number,
processCode: number
): any {
return axios({
url: `/projects/${projectCode}/process-definition/query-task-definition-list`,
method: 'get',
params: {
processDefinitionCode: processCode
}
})
}
export function queryVersions(
params: PageReq,
code: number,
processCode: number
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/versions`,
method: 'get',
params
})
}
export function switchVersion(
code: number,
processCode: number,
version: number
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/versions/${version}`,
method: 'get'
})
}
export function deleteVersion(
code: number,
processCode: number,
version: number
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/versions/${version}`,
method: 'delete'
})
}
export function viewTree(
code: number,
processCode: number,
params: LimitReq
): any {
return axios({
url: `/projects/${code}/process-definition/${processCode}/view-tree`,
method: 'get',
params
})
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
defineComponent,
PropType,
ref,
computed,
onMounted,
watch,
getCurrentInstance
} from 'vue'
import Modal from '@/components/modal'
import { useI18n } from 'vue-i18n'
import {
NForm,
NFormItem,
NInput,
NSelect,
NSwitch,
NInputNumber,
NDynamicInput,
NCheckbox
} from 'naive-ui'
import { queryTenantList } from '@/service/modules/tenants'
import { useRoute } from 'vue-router'
import { verifyName } from '@/service/modules/process-definition'
import './x6-style.scss'
import { positiveIntegerRegex } from '@/utils/regex'
import type { SaveForm, WorkflowDefinition, WorkflowInstance } from './types'
const props = {
visible: {
type: Boolean as PropType<boolean>,
default: false
},
// If this prop is passed, it means from definition detail
definition: {
type: Object as PropType<WorkflowDefinition>,
default: undefined
},
instance: {
type: Object as PropType<WorkflowInstance>,
default: undefined
}
}
interface Tenant {
tenantCode: string
id: number
}
export default defineComponent({
name: 'dag-save-modal',
props,
emits: ['update:show', 'save'],
setup(props, context) {
const route = useRoute()
const { t } = useI18n()
const projectCode = Number(route.params.projectCode)
const tenants = ref<Tenant[]>([])
const tenantsDropdown = computed(() => {
if (tenants.value) {
return tenants.value
.map((t) => ({
label: t.tenantCode,
value: t.tenantCode
}))
.concat({ label: 'default', value: 'default' })
}
return []
})
onMounted(() => {
queryTenantList().then((res: any) => {
tenants.value = res
})
})
const formValue = ref<SaveForm>({
name: '',
description: '',
tenantCode: 'default',
executionType: 'PARALLEL',
timeoutFlag: false,
timeout: 0,
globalParams: [],
release: false,
sync: false
})
const formRef = ref()
const rule = {
name: {
required: true,
message: t('project.dag.dag_name_empty')
},
timeout: {
validator() {
if (
formValue.value.timeoutFlag &&
!positiveIntegerRegex.test(String(formValue.value.timeout))
) {
return new Error(t('project.dag.positive_integer'))
}
}
},
globalParams: {
validator() {
const props = new Set()
const keys = formValue.value.globalParams.map((item) => item.key)
const keysSet = new Set(keys)
if (keysSet.size !== keys.length) {
return new Error(t('project.dag.prop_repeat'))
}
for (const param of formValue.value.globalParams) {
const prop = param.value
if (!prop) {
return new Error(t('project.dag.prop_empty'))
}
props.add(prop)
}
}
}
}
const onSubmit = () => {
formRef.value.validate(async (valid: any) => {
if (!valid) {
const params = {
name: formValue.value.name,
code: props.definition?.processDefinition.code
} as { name: string; code?: number }
if (
props.definition?.processDefinition.name !== formValue.value.name
) {
verifyName(params, projectCode).then(() =>
context.emit('save', formValue.value)
)
} else {
context.emit('save', formValue.value)
}
}
})
}
const onCancel = () => {
context.emit('update:show', false)
}
const updateModalData = () => {
const process = props.definition?.processDefinition
if (process) {
formValue.value.name = process.name
formValue.value.description = process.description
formValue.value.tenantCode = process.tenantCode || 'default'
formValue.value.executionType = process.executionType || 'PARALLEL'
if (process.timeout && process.timeout > 0) {
formValue.value.timeoutFlag = true
formValue.value.timeout = process.timeout
}
formValue.value.globalParams = process.globalParamList.map((param) => ({
key: param.prop,
value: param.value
}))
}
}
const trim = getCurrentInstance()?.appContext.config.globalProperties.trim
onMounted(() => updateModalData())
watch(
() => props.definition?.processDefinition,
() => updateModalData()
)
return () => (
<Modal
show={props.visible}
title={t('project.dag.basic_info')}
onConfirm={onSubmit}
onCancel={onCancel}
autoFocus={false}
>
<NForm model={formValue.value} rules={rule} ref={formRef}>
<NFormItem label={t('project.dag.workflow_name')} path='name'>
<NInput
allowInput={trim}
v-model:value={formValue.value.name}
class='input-name'
/>
</NFormItem>
<NFormItem label={t('project.dag.description')} path='description'>
<NInput
allowInput={trim}
type='textarea'
v-model:value={formValue.value.description}
class='input-description'
/>
</NFormItem>
<NFormItem label={t('project.dag.tenant')} path='tenantCode'>
<NSelect
options={tenantsDropdown.value}
v-model:value={formValue.value.tenantCode}
class='btn-select-tenant-code'
/>
</NFormItem>
<NFormItem label={t('project.dag.timeout_alert')} path='timeoutFlag'>
<NSwitch v-model:value={formValue.value.timeoutFlag} />
</NFormItem>
{formValue.value.timeoutFlag && (
<NFormItem showLabel={false} path='timeout'>
<NInputNumber
v-model:value={formValue.value.timeout}
show-button={false}
min={0}
v-slots={{
suffix: () => t('project.dag.minute')
}}
/>
</NFormItem>
)}
{!props.instance && (
<NFormItem
label={t('project.dag.process_execute_type')}
path='executionType'
>
<NSelect
options={[
{ value: 'PARALLEL', label: t('project.dag.parallel') },
{ value: 'SERIAL_WAIT', label: t('project.dag.serial_wait') },
{
value: 'SERIAL_DISCARD',
label: t('project.dag.serial_discard')
},
{
value: 'SERIAL_PRIORITY',
label: t('project.dag.serial_priority')
}
]}
v-model:value={formValue.value.executionType}
/>
</NFormItem>
)}
<NFormItem
label={t('project.dag.global_variables')}
path='globalParams'
>
<NDynamicInput
v-model:value={formValue.value.globalParams}
preset='pair'
key-placeholder={t('project.dag.key')}
value-placeholder={t('project.dag.value')}
class='input-global-params'
/>
</NFormItem>
{props.definition && !props.instance && (
<NFormItem path='timeoutFlag' showLabel={false}>
<NCheckbox v-model:checked={formValue.value.release}>
{t('project.dag.online_directly')}
</NCheckbox>
</NFormItem>
)}
{props.instance && (
<NFormItem path='sync' showLabel={false}>
<NCheckbox v-model:checked={formValue.value.sync}>
{t('project.dag.update_directly')}
</NCheckbox>
</NFormItem>
)}
</NForm>
</Modal>
)
}
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册