Browse Source

[Feature][UI Next] Add the form of sql task to the module of next ui. (#8471)

* add the form of sql task

* add the form of sql task

* add the form of sql task

* add the component of input group

* add the component of input group

* The form of sql task is done
3.0.0/version-upgrade
calvin 3 years ago committed by GitHub
parent
commit
dd3e2905dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      dolphinscheduler-ui-next/docs/e2e/security.md
  2. 1
      dolphinscheduler-ui-next/src/components/form/fields/index.ts
  3. 139
      dolphinscheduler-ui-next/src/components/form/fields/multi-input.ts
  4. 1
      dolphinscheduler-ui-next/src/components/form/types.ts
  5. 12
      dolphinscheduler-ui-next/src/locales/modules/en_US.ts
  6. 12
      dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
  7. 2
      dolphinscheduler-ui-next/src/views/datasource/list/use-form.ts
  8. 4
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts
  9. 114
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-datasource-type.ts
  10. 86
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-datasource.ts
  11. 72
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql-type.ts
  12. 214
      dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts
  13. 10
      dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts
  14. 95
      dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-sql.ts
  15. 12
      dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts
  16. 9
      dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts

4
dolphinscheduler-ui-next/docs/e2e/security.md

@ -12,7 +12,7 @@ class:
#### tenant manage
| check | class |
| ------------------ |--------------------|
| ------------------ | ------------------ |
| :white_check_mark: | items |
| | el-popconfirm |
| | el-button--primary |
@ -29,7 +29,7 @@ class:
#### user manage
| check | class |
| ------------------ |---------------------|
| ------------------ | ------------------- |
| :white_check_mark: | items |
| | el-popconfirm |
| | el-button--primary |

1
dolphinscheduler-ui-next/src/components/form/fields/index.ts

@ -24,3 +24,4 @@ export { renderInputNumber } from './input-number'
export { renderSelect } from './select'
export { renderCheckbox } from './checkbox'
export { renderTreeSelect } from './tree-select'
export { renderMultiInput } from './multi-input'

139
dolphinscheduler-ui-next/src/components/form/fields/multi-input.ts

@ -0,0 +1,139 @@
/*
* 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, h, unref, renderSlot } from 'vue'
import { useFormItem } from 'naive-ui/es/_mixins'
import {
NFormItemGi,
NSpace,
NButton,
NGrid,
NGridItem,
NInput
} from 'naive-ui'
import { PlusOutlined, DeleteOutlined } from '@vicons/antd'
import type { IJsonItem, FormItemRule } from '../types'
const MultiInput = defineComponent({
name: 'MultiInput',
emits: ['add'],
setup(props, ctx) {
const formItem = useFormItem({})
const onAdd = () => void ctx.emit('add')
return { onAdd, disabled: formItem.mergedDisabledRef }
},
render() {
const { disabled, $slots, onAdd } = this
return h(
NSpace,
{ vertical: true, style: { width: '100%' } },
{
default: () => {
return [
renderSlot($slots, 'default', { disabled }),
h(
NButton,
{
circle: true,
size: 'small',
type: 'info',
disabled,
onClick: onAdd
},
{
icon: () => h(PlusOutlined)
}
)
]
}
}
)
}
})
export function renderMultiInput(
item: IJsonItem,
fields: { [field: string]: any },
rules: { [key: string]: FormItemRule }[]
) {
let ruleItem: { [key: string]: FormItemRule } = {}
// the fields is the data of the task definition.
// the item is the options of this component in the form.
const getChild = (value: string, i: number) => {
return h(
NFormItemGi,
{
showLabel: false,
path: `${item.field}[${i}]`,
span: unref(item.span)
},
() =>
h(NInput, {
...item.props,
value: value,
onUpdateValue: (value: string) => void (fields[item.field][i] = value)
})
)
}
//initialize the component by using data
const getChildren = ({ disabled }: { disabled: boolean }) =>
fields[item.field].map((value: string, i: number) => {
return h(NGrid, { xGap: 10 }, () => [
getChild(value, i),
h(
NGridItem,
{
span: 2
},
() =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'small',
disabled,
onClick: () => {
fields[item.field].splice(i, 1)
}
},
{
icon: () => h(DeleteOutlined)
}
)
)
])
})
return h(
MultiInput,
{
name: item.field,
onAdd: () => {
fields[item.field].push('')
}
},
{
default: getChildren
}
)
}

1
dolphinscheduler-ui-next/src/components/form/types.ts

@ -34,6 +34,7 @@ type IType =
| 'select'
| 'checkbox'
| 'tree-select'
| 'multi-input'
interface IOption extends SelectOption, TreeSelectOption {
label: string

12
dolphinscheduler-ui-next/src/locales/modules/en_US.ts

@ -672,7 +672,17 @@ const project = {
body_not_contains: 'Content does not contain',
http_parameters_position: 'Http Parameters Position',
target_task_name: 'Target Task Name',
target_task_name_tips: 'Please enter the Pigeon task name'
target_task_name_tips: 'Please enter the Pigeon task name',
datasource_type: 'Datasource types',
datasource: 'Datasource instances',
sql_type: 'SQL Type',
sql_type_query: 'Query',
sql_type_non_query: 'Non Query',
sql_statement: 'SQL Statement',
pre_sql_statement: 'Pre SQL Statement',
post_sql_statement: 'Post SQL Statement',
sql_input_placeholder: 'Please enter non-query sql.',
sql_empty_tips: 'The sql can not be empty.'
}
}

12
dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts

@ -664,7 +664,17 @@ const project = {
body_not_contains: '内容不包含',
http_parameters_position: '参数位置',
target_task_name: '目标任务名',
target_task_name_tips: '请输入Pigeon任务名'
target_task_name_tips: '请输入Pigeon任务名',
datasource_type: '数据源类型',
datasource: '数据源实例',
sql_type: 'SQL类型',
sql_type_query: '查询',
sql_type_non_query: '非查询',
sql_statement: 'SQL语句',
pre_sql_statement: '前置SQL语句',
post_sql_statement: '后置SQL语句',
sql_input_placeholder: '请输入非查询SQL语句',
sql_empty_tips: '语句不能为空'
}
}

2
dolphinscheduler-ui-next/src/views/datasource/list/use-form.ts

@ -205,8 +205,6 @@ const datasourceType: IDataBaseOptionKeys = {
}
}
console.log(Object.values(datasourceType))
export const datasourceTypeList: IDataBaseOption[] = Object.values(
datasourceType
).map((item) => {

4
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/index.ts

@ -30,9 +30,13 @@ export { useTaskType } from './use-task-type'
export { useProcessName } from './use-process-name'
export { useChildNode } from './use-child-node'
export { useTargetTaskName } from './use-target-task-name'
export { useDatasourceType } from './use-datasource-type'
export { useDatasource } from './use-datasource'
export { useSqlType } from './use-sql-type'
export { useShell } from './use-shell'
export { useSpark } from './use-spark'
export { useMr } from './use-mr'
export { useFlink } from './use-flink'
export { useHttp } from './use-http'
export { useSql } from './use-sql'

114
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-datasource-type.ts

@ -0,0 +1,114 @@
/*
* 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 { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
import { number } from 'echarts'
export function useDatasourceType(model: { [field: string]: any }): IJsonItem {
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])
const loading = ref(false)
const datasourceTypes = [
{
id: 0,
code: 'MYSQL',
disabled: false
},
{
id: 1,
code: 'POSTGRESQL',
disabled: false
},
{
id: 2,
code: 'HIVE',
disabled: false
},
{
id: 3,
code: 'SPARK',
disabled: false
},
{
id: 4,
code: 'CLICKHOUSE',
disabled: false
},
{
id: 5,
code: 'ORACLE',
disabled: false
},
{
id: 6,
code: 'SQLSERVER',
disabled: false
},
{
id: 7,
code: 'DB2',
disabled: false
},
{
id: 8,
code: 'PRESTO',
disabled: false
}
]
const getDatasourceTypes = async () => {
if (loading.value) return
loading.value = true
try {
options.value = datasourceTypes
.filter((item) => !item.disabled)
.map((item) => ({ label: item.code, value: item.code }))
loading.value = false
} catch (err) {
loading.value = false
}
}
const onChange = (type: string) => {
model.type = type
}
onMounted(() => {
getDatasourceTypes()
})
return {
type: 'select',
field: 'datasourceType',
span: 12,
name: t('project.node.datasource_type'),
props: {
loading: loading,
'on-update:value': onChange
},
options: options,
validate: {
trigger: ['input', 'blur'],
required: true,
message: t('project.node.worker_group_tips')
},
value: model.type
}
}

86
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-datasource.ts

@ -0,0 +1,86 @@
/*
* 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 { ref, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryDataSourceList } from '@/service/modules/data-source'
import type { IJsonItem } from '../types'
import { TypeReq } from '@/service/modules/data-source/types'
import { find } from 'lodash'
export function useDatasource(model: { [field: string]: any }): IJsonItem {
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])
const loading = ref(false)
const defaultValue = ref(null)
const getDatasources = async () => {
if (loading.value) return
loading.value = true
try {
await refreshOptions()
loading.value = false
} catch (err) {
loading.value = false
}
}
const refreshOptions = async () => {
const params = { type: model.type } as TypeReq
const res = await queryDataSourceList(params)
defaultValue.value = null
options.value = []
res.map((item: any) => {
options.value.push({ label: item.name, value: String(item.id) })
})
if (options.value && model.datasource) {
let item = find(options.value, { value: String(model.datasource) })
if (!item) {
model.datasource = null
}
}
}
watch(
() => model.type,
() => {
if (model.type) {
refreshOptions()
}
}
)
onMounted(() => {
getDatasources()
})
return {
type: 'select',
field: 'datasource',
span: 12,
name: t('project.node.datasource'),
props: {
loading: loading
},
options: options,
validate: {
trigger: ['input', 'blur'],
required: true
}
}
}

72
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql-type.ts

@ -0,0 +1,72 @@
/*
* 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 { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import type { IJsonItem } from '../types'
export function useSqlType(model: { [field: string]: any }): IJsonItem {
const { t } = useI18n()
const options = ref([] as { label: string; value: string }[])
const loading = ref(false)
const sqlTypes = [
{
id: '0',
code: t('project.node.sql_type_query')
},
{
id: '1',
code: t('project.node.sql_type_non_query')
}
]
const getSqlTypes = async () => {
if (loading.value) return
loading.value = true
try {
options.value = sqlTypes.map((item) => ({
label: item.code,
value: item.id
}))
loading.value = false
} catch (err) {
loading.value = false
}
}
onMounted(() => {
getSqlTypes()
})
return {
type: 'select',
field: 'sqlType',
span: 12,
name: t('project.node.sql_type'),
props: {
loading: loading
},
options: options,
validate: {
trigger: ['input', 'blur'],
required: true
},
value: '0'
}
}

214
dolphinscheduler-ui-next/src/views/projects/task/components/node/fields/use-sql.ts

@ -0,0 +1,214 @@
/*
* 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 { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { queryResourceList } from '@/service/modules/resources'
import type { IJsonItem } from '../types'
export function useSql(model: { [field: string]: any }): IJsonItem[] {
const { t } = useI18n()
const options = ref([])
const loading = ref(false)
const getResourceList = async () => {
if (loading.value) return
loading.value = true
try {
const res = await queryResourceList({ type: 'FILE' })
removeUselessChildren(res)
options.value = res || []
loading.value = false
} catch (err) {
loading.value = false
}
}
onMounted(() => {
getResourceList()
})
return [
{
type: 'editor',
field: 'sql',
name: t('project.node.sql_statement'),
validate: {
trigger: ['input', 'trigger'],
required: true,
message: t('project.node.sql_empty_tips')
}
},
{
type: 'tree-select',
field: 'resourceList',
name: t('project.node.resources'),
options,
props: {
multiple: true,
checkable: true,
cascade: true,
showPath: true,
checkStrategy: 'child',
placeholder: t('project.node.resources_tips'),
keyField: 'id',
labelField: 'name',
loading
}
},
{
type: 'custom-parameters',
field: 'localParams',
name: t('project.node.custom_parameters'),
children: [
{
type: 'input',
field: 'prop',
span: 6,
props: {
placeholder: t('project.node.prop_tips'),
maxLength: 256
},
validate: {
trigger: ['input', 'blur'],
required: true,
validator(validate: any, value: string) {
if (!value) {
return new Error(t('project.node.prop_tips'))
}
const sameItems = model.localParams.filter(
(item: { prop: string }) => item.prop === value
)
if (sameItems.length > 1) {
return new Error(t('project.node.prop_repeat'))
}
}
}
},
{
type: 'select',
field: 'direct',
span: 4,
options: DIRECT_LIST,
value: 'IN'
},
{
type: 'select',
field: 'type',
span: 6,
options: TYPE_LIST,
value: 'VARCHAR'
},
{
type: 'input',
field: 'value',
span: 6,
props: {
placeholder: t('project.node.value_tips'),
maxLength: 256
}
}
]
},
{
type: 'multi-input',
field: 'preStatements',
name: t('project.node.pre_sql_statement'),
span: 22,
props: {
placeholder: t('project.node.sql_input_placeholder'),
type: 'textarea',
autosize: { minRows: 1 }
}
},
{
type: 'multi-input',
field: 'postStatements',
name: t('project.node.post_sql_statement'),
span: 22,
props: {
placeholder: t('project.node.sql_input_placeholder'),
type: 'textarea',
autosize: { minRows: 1 }
}
}
]
}
function removeUselessChildren(list: { children?: [] }[]) {
if (!list.length) return
list.forEach((item) => {
if (!item.children) return
if (item.children.length === 0) {
delete item.children
return
}
removeUselessChildren(item.children)
})
}
export const TYPE_LIST = [
{
value: 'VARCHAR',
label: 'VARCHAR'
},
{
value: 'INTEGER',
label: 'INTEGER'
},
{
value: 'LONG',
label: 'LONG'
},
{
value: 'FLOAT',
label: 'FLOAT'
},
{
value: 'DOUBLE',
label: 'DOUBLE'
},
{
value: 'DATE',
label: 'DATE'
},
{
value: 'TIME',
label: 'TIME'
},
{
value: 'TIMESTAMP',
label: 'TIMESTAMP'
},
{
value: 'BOOLEAN',
label: 'BOOLEAN'
}
]
export const DIRECT_LIST = [
{
value: 'IN',
label: 'IN'
},
{
value: 'OUT',
label: 'OUT'
}
]

10
dolphinscheduler-ui-next/src/views/projects/task/components/node/format-data.ts

@ -67,6 +67,15 @@ export function formatParams(data: INodeData): {
taskParams.socketTimeout = data.socketTimeout
}
if (data.taskType === 'SQL') {
taskParams.type = data.type
taskParams.datasource = data.datasource
taskParams.sql = data.sql
taskParams.sqlType = data.sqlType
taskParams.preStatements = data.preStatements
taskParams.postStatements = data.postStatements
}
const params = {
processDefinitionCode: data.processName ? String(data.processName) : '',
upstreamCodes: data?.preTasks?.join(','),
@ -125,6 +134,7 @@ export function formatModel(data: ITaskData) {
timeoutNotifyStrategy: [data.timeoutNotifyStrategy] || [],
localParams: data.taskParams?.localParams || []
} as INodeData
if (data.timeoutNotifyStrategy === 'WARNFAILED') {
params.timeoutNotifyStrategy = ['WARN', 'FAILED']
}

95
dolphinscheduler-ui-next/src/views/projects/task/components/node/tasks/use-sql.ts

@ -0,0 +1,95 @@
/*
* 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 { reactive } from 'vue'
import * as Fields from '../fields/index'
import type { IJsonItem, INodeData } from '../types'
import { ITaskData } from '../types'
export function useSql({
projectCode,
from = 0,
readonly,
data
}: {
projectCode: number
from?: number
readonly?: boolean
data?: ITaskData
}) {
const model = reactive({
name: '',
taskType: 'SQL',
flag: 'YES',
description: '',
timeoutFlag: false,
localParams: [],
environmentCode: null,
failRetryInterval: 1,
failRetryTimes: 0,
workerGroup: 'default',
delayTime: 0,
timeout: 30,
type: data?.taskParams?.type ? data?.taskParams?.type : 'MYSQL',
datasource: data?.taskParams?.datasource,
sql: data?.taskParams?.sql,
sqlType: data?.taskParams?.sqlType,
preStatements: data?.taskParams?.preStatements
? data?.taskParams?.preStatements
: [],
postStatements: data?.taskParams?.postStatements
? data?.taskParams?.postStatements
: []
} as INodeData)
let extra: IJsonItem[] = []
if (from === 1) {
extra = [
Fields.useTaskType(model, readonly),
Fields.useProcessName({
model,
projectCode,
isCreate: !data?.id,
from,
processName: data?.processName,
code: data?.code
})
]
}
return {
json: [
Fields.useName(),
...extra,
Fields.useRunFlag(),
Fields.useDescription(),
Fields.useTaskPriority(),
Fields.useWorkerGroup(),
Fields.useEnvironmentName(model, !model.id),
...Fields.useTaskGroup(model, projectCode),
...Fields.useFailed(),
Fields.useDelayTime(model),
...Fields.useTimeoutAlarm(model),
Fields.useDatasourceType(model),
Fields.useDatasource(model),
Fields.useSqlType(model),
...Fields.useSql(model),
Fields.usePreTasks(model)
] as IJsonItem[],
model
}
}

12
dolphinscheduler-ui-next/src/views/projects/task/components/node/types.ts

@ -70,6 +70,12 @@ interface ITaskParams {
condition?: string
connectTimeout?: number
socketTimeout?: number
type?: string
datasource?: string
sql?: string
sqlType?: string
preStatements?: string[]
postStatements?: string[]
}
type ITaskType = TaskType
@ -99,6 +105,12 @@ interface INodeData extends Omit<ITaskParams, 'resourceList' | 'mainJar'> {
resourceList?: number[]
mainJar?: number
timeoutSetting?: boolean
type?: string
datasource?: string
sql?: string
sqlType?: string
preStatements?: string[]
postStatements?: string[]
}
interface ITaskData

9
dolphinscheduler-ui-next/src/views/projects/task/components/node/use-task.ts

@ -23,6 +23,7 @@ import { usePython } from './tasks/use-python'
import { useSpark } from './tasks/use-spark'
import { useMr } from './tasks/use-mr'
import { useHttp } from './tasks/use-http'
import { useSql } from './tasks/use-sql'
import { IJsonItem, INodeData, ITaskData } from './types'
export function useTask({
@ -102,5 +103,13 @@ export function useTask({
data
})
}
if (taskType === 'SQL') {
node = useSql({
projectCode,
from,
readonly,
data
})
}
return node
}

Loading…
Cancel
Save