refactor: 数据源插件优化

This commit is contained in:
junjun 2024-07-18 13:11:37 +08:00
parent 95fd1f5295
commit 7cf6db3d94
16 changed files with 890 additions and 59 deletions

View File

@ -90,7 +90,7 @@ public class DatasetDataManage {
}
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
datasourceSchemaDTO.setSchemaAlias(String.format(SQLConstants.SCHEMA, datasourceSchemaDTO.getId()));
Provider provider = ProviderFactory.getDefaultProvider();
Provider provider = ProviderFactory.getProvider(coreDatasource.getType());
DatasourceRequest datasourceRequest = new DatasourceRequest();
datasourceRequest.setDsList(Map.of(datasourceSchemaDTO.getId(), datasourceSchemaDTO));
@ -149,9 +149,9 @@ public class DatasetDataManage {
dto.setChecked(defaultStatus);
dto.setType(ele.getType());
int deType = FieldUtils.transType2DeType(ele.getType());
dto.setDeExtractType(deType);
dto.setDeType(deType);
dto.setGroupType(FieldUtils.transDeType2DQ(deType));
dto.setDeExtractType(ObjectUtils.isEmpty(ele.getDeExtractType()) ? deType : ele.getDeExtractType());
dto.setDeType(ObjectUtils.isEmpty(ele.getDeType()) ? deType : ele.getDeType());
dto.setGroupType(FieldUtils.transDeType2DQ(dto.getDeType()));
dto.setExtField(0);
dto.setDescription(StringUtils.isNotEmpty(ele.getName()) ? ele.getName() : null);
return dto;

View File

@ -1,13 +1,5 @@
package io.dataease.dataset.manage;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.view.dto.ChartExtFilterDTO;
import io.dataease.extensions.view.dto.ChartExtRequest;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.api.dataset.union.*;
import io.dataease.api.permissions.auth.dto.BusiPerCheckDTO;
import io.dataease.commons.utils.SqlparserUtils;
@ -22,6 +14,16 @@ import io.dataease.datasource.manage.EngineManage;
import io.dataease.engine.constant.ExtFieldConstant;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.DsTypeDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.PluginDatasourceType;
import io.dataease.extensions.view.dto.ChartExtFilterDTO;
import io.dataease.extensions.view.dto.ChartExtRequest;
import io.dataease.extensions.view.dto.SqlVariableDetails;
import io.dataease.i18n.Translator;
import io.dataease.system.manage.CorePermissionManage;
import io.dataease.utils.BeanUtils;
@ -135,7 +137,7 @@ public class DatasetSQLManage {
prefix = "`";
suffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
@ -188,7 +190,7 @@ public class DatasetSQLManage {
tablePrefix = "`";
tableSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
tablePrefix = datasourceType.getPrefix();
tableSuffix = datasourceType.getSuffix();
}
@ -214,7 +216,7 @@ public class DatasetSQLManage {
pPrefix = "`";
pSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, parentDs.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, parentDs.getDatasourceId());
pPrefix = datasourceType.getPrefix();
pSuffix = datasourceType.getSuffix();
}
@ -226,7 +228,7 @@ public class DatasetSQLManage {
cPrefix = "`";
cSuffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, currentDs1.getDatasourceId());
cPrefix = datasourceType.getPrefix();
cSuffix = datasourceType.getSuffix();
}
@ -305,7 +307,7 @@ public class DatasetSQLManage {
prefix = "`";
suffix = "`";
} else {
DatasourceConfiguration.DatasourceType datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
DsTypeDTO datasourceType = getDatasourceType(dsMap, datasetTable.getDatasourceId());
prefix = datasourceType.getPrefix();
suffix = datasourceType.getSuffix();
}
@ -348,7 +350,7 @@ public class DatasetSQLManage {
}
}
private DatasourceConfiguration.DatasourceType getDatasourceType(Map<Long, DatasourceSchemaDTO> dsMap, Long datasourceId) {
private DsTypeDTO getDatasourceType(Map<Long, DatasourceSchemaDTO> dsMap, Long datasourceId) {
DatasourceSchemaDTO datasourceSchemaDTO = dsMap.get(datasourceId);
String type;
if (datasourceSchemaDTO == null) {
@ -360,7 +362,17 @@ public class DatasetSQLManage {
} else {
type = datasourceSchemaDTO.getType();
}
return DatasourceConfiguration.DatasourceType.valueOf(type);
if (Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList().contains(type)) {
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(type);
DsTypeDTO dto = new DsTypeDTO();
BeanUtils.copyBean(dto, datasourceType);
return dto;
} else {
PluginDatasourceType.DatasourceType datasourceType = PluginDatasourceType.DatasourceType.valueOf(type);
DsTypeDTO dto = new DsTypeDTO();
BeanUtils.copyBean(dto, datasourceType);
return dto;
}
}
public String subPrefixSuffixChar(String str) {

View File

@ -1,8 +1,8 @@
package io.dataease.dataset.utils;
import io.dataease.extensions.datasource.dto.DatasourceSchemaDTO;
import io.dataease.extensions.datasource.dto.DsTypeDTO;
import io.dataease.extensions.datasource.model.SQLObj;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.utils.Md5Utils;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.commons.lang3.StringUtils;
@ -39,7 +39,7 @@ public class TableUtils {
return "C_" + Md5Utils.md5(fieldName);
}
public static String getTableAndAlias(SQLObj sqlObj, DatasourceConfiguration.DatasourceType datasourceType, boolean isCross) {
public static String getTableAndAlias(SQLObj sqlObj, DsTypeDTO datasourceType, boolean isCross) {
String schema = "";
String prefix = "";
String suffix = "";

View File

@ -11,6 +11,8 @@ import io.dataease.datasource.type.*;
import io.dataease.engine.constant.SQLConstants;
import io.dataease.exception.DEException;
import io.dataease.extensions.datasource.dto.*;
import io.dataease.extensions.datasource.provider.DriverShim;
import io.dataease.extensions.datasource.provider.ExtendedJdbcClassLoader;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.i18n.Translator;
@ -24,7 +26,6 @@ import org.apache.calcite.adapter.jdbc.JdbcSchema;
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlDialect;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@ -442,6 +443,7 @@ public class CalciteProvider extends Provider {
return list;
}
@Override
public void hidePW(DatasourceDTO datasourceDTO) {
DatasourceConfiguration configuration = null;
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(datasourceDTO.getType());
@ -1047,20 +1049,6 @@ public class CalciteProvider extends Provider {
}
}
public Statement getStatement(Connection connection, int queryTimeout) {
if (connection == null) {
DEException.throwException("Failed to get connection!");
}
Statement stat = null;
try {
stat = connection.createStatement();
stat.setQueryTimeout(queryTimeout);
} catch (Exception e) {
DEException.throwException(e.getMessage());
}
return stat;
}
protected boolean isDefaultClassLoader(String customDriver) {
return StringUtils.isEmpty(customDriver) || customDriver.equalsIgnoreCase("default");
}

View File

@ -36,6 +36,7 @@ import io.dataease.extensions.datasource.dto.*;
import io.dataease.extensions.datasource.factory.ProviderFactory;
import io.dataease.extensions.datasource.provider.Provider;
import io.dataease.extensions.datasource.vo.DatasourceConfiguration;
import io.dataease.extensions.datasource.vo.PluginDatasourceType;
import io.dataease.i18n.Translator;
import io.dataease.job.schedule.CheckDsStatusJob;
import io.dataease.job.schedule.ScheduleManager;
@ -575,7 +576,8 @@ public class DatasourceServer implements DatasourceApi {
}
} else {
if (hidePw) {
calciteProvider.hidePW(datasourceDTO);
Provider provider = ProviderFactory.getProvider(datasourceDTO.getType());
provider.hidePW(datasourceDTO);
}
}
@ -851,7 +853,8 @@ public class DatasourceServer implements DatasourceApi {
}
private void preCheckDs(DatasourceDTO datasource) throws DEException {
if (!datasourceTypes().stream().map(DatasourceConfiguration.DatasourceType::getType).toList().contains(datasource.getType())) {
if (!datasourceTypes().stream().map(DatasourceConfiguration.DatasourceType::getType).toList().contains(datasource.getType())
&& !Arrays.stream(PluginDatasourceType.DatasourceType.values()).map(PluginDatasourceType.DatasourceType::getType).toList().contains(datasource.getType())) {
DEException.throwException("Datasource type not supported.");
}
}
@ -867,7 +870,8 @@ public class DatasourceServer implements DatasourceApi {
if (coreDatasource.getType().equals("API")) {
status = ApiUtils.checkStatus(datasourceRequest);
} else {
status = calciteProvider.checkStatus(datasourceRequest);
Provider provider = ProviderFactory.getProvider(coreDatasource.getType());
status = provider.checkStatus(datasourceRequest);
}
coreDatasource.setStatus(status);
} catch (Exception e) {

View File

@ -1200,6 +1200,21 @@ const treeProps = {
return (!data.children?.length && !data.leaf) || data.extraFlag < 0
}
}
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
}
const getDsIcon = data => {
if (pluginDs?.value.length === 0) return null
if (!data.leaf) return null
const arr = pluginDs.value.filter(ele => {
return ele.type === data.type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconName = data => {
if (!data.leaf) return 'dv-folder'
return `${data.type}-ds`
@ -1280,7 +1295,10 @@ const getDsIconName = data => {
<template #default="{ data: { name, leaf, type, extraFlag } }">
<div class="flex-align-center icon">
<el-icon>
<icon :name="getDsIconName({ leaf, type })"></icon>
<icon
:static-content="getDsIcon({ leaf, type })"
:name="getDsIconName({ leaf, type })"
></icon>
</el-icon>
<span v-if="!leaf || extraFlag > -1">{{ name }}</span>
<el-tooltip effect="dark" v-else :content="`无效数据源:${name}`" placement="top">
@ -1975,6 +1993,10 @@ const getDsIconName = data => {
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</template>
<style lang="less" scoped>

View File

@ -1,6 +1,8 @@
<script lang="ts" setup>
import { shallowRef, PropType, computed } from 'vue'
import { dsTypes, typeList, nameMap } from './option'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { XpackComponent } from '@/components/plugin'
export type DsType = 'OLTP' | 'OLAP' | 'DL' | 'OTHER' | 'LOCAL' | 'latestUse' | 'all'
const props = defineProps({
@ -80,6 +82,25 @@ const getDatasourceTypes = () => {
})
}
getDatasourceTypes()
const loadDsPlugin = data => {
data.forEach(item => {
const { name, category, type, icon, extraParams, staticMap } = item
const node = {
name,
catalog: category,
type,
icon,
extraParams,
isPlugin: true,
staticMap
}
const index = typeList.findIndex(ele => ele === node.catalog)
if (index !== -1) {
databaseList.value[index].push(node)
}
})
}
const emits = defineEmits(['selectDsType'])
const selectDs = ({ type }) => {
emits('selectDsType', type)
@ -95,12 +116,18 @@ const selectDs = ({ type }) => {
<div class="item-container">
<div v-for="db in ele.dbList" :key="db.type" class="db-card" @click="selectDs(db)">
<el-icon class="icon-border">
<Icon :name="`${db.type}-ds`"></Icon>
<Icon v-if="db['isPlugin']" :static-content="db.icon"></Icon>
<Icon v-else :name="`${db.type}-ds`"></Icon>
</el-icon>
<p class="db-name">{{ db.name }}</p>
</div>
</div>
</template>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</template>

View File

@ -0,0 +1,629 @@
<script lang="ts" setup>
import { ref, reactive, toRefs, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
import { cloneDeep } from 'lodash-es'
import type { Configuration, ApiConfiguration, SyncSetting } from './option'
import { Base64 } from 'js-base64'
import { ElMessage } from 'element-plus-secondary'
const { t } = useI18n()
const prop = defineProps({
form: {
required: false,
default() {
return reactive<{
id: number
name: string
desc: string
type: string
syncSetting?: SyncSetting
configuration?: Configuration
apiConfiguration?: ApiConfiguration[]
paramsConfiguration?: ApiConfiguration[]
}>({
id: 0,
name: '',
desc: '',
type: 'API',
apiConfiguration: []
})
},
type: Object
},
activeStep: {
required: false,
default: 1,
type: Number
}
})
const { form, activeStep } = toRefs(prop)
const state = reactive({
itemRef: []
})
const schemas = ref([])
const dsForm = ref<FormInstance>()
const defaultRule = {
name: [
{
required: true,
message: t('datasource.input_name'),
trigger: 'blur'
},
{
min: 2,
max: 64,
message: t('datasource.input_limit_2_25', [2, 64]),
trigger: 'blur'
}
]
}
const rule = ref<FormRules>(cloneDeep(defaultRule))
const api_table_title = ref('')
const editApiItem = ref()
const defaultApiItem = {
name: '',
deTableName: '',
url: '',
type: '',
serialNumber: 0,
method: 'GET',
request: {
headers: [{}],
arguments: [],
body: {
type: '',
raw: '',
kvs: []
},
authManager: {
verification: '',
username: '',
password: ''
}
},
fields: []
}
const initForm = type => {
form.value.configuration = {
dataBase: '',
jdbcUrl: '',
urlType: 'hostName',
extraParams: '',
username: '',
password: '',
host: '',
authMethod: '',
port: '',
initialPoolSize: 5,
minPoolSize: 5,
maxPoolSize: 5,
queryTimeout: 30
}
schemas.value = []
rule.value = cloneDeep(defaultRule)
setRules()
form.value.type = type
setTimeout(() => {
dsForm.value.clearValidate()
}, 0)
}
const setRules = () => {
const configRules = {
'configuration.jdbcUrl': [
{
required: true,
message: t('datasource.please_input_jdbc_url'),
trigger: 'blur'
}
],
'configuration.dataBase': [
{
required: true,
message: t('datasource.please_input_data_base'),
trigger: 'blur'
}
],
'configuration.authMethod': [
{
required: true,
message: t('datasource.please_select_oracle_type'),
trigger: 'blur'
}
],
'configuration.username': [
{
required: true,
message: t('datasource.please_input_user_name'),
trigger: 'blur'
}
],
'configuration.password': [
{
required: true,
message: t('datasource.please_input_password'),
trigger: 'blur'
}
],
'configuration.host': [
{
required: true,
message: t('datasource._ip_address'),
trigger: 'blur'
}
],
'configuration.extraParams': [
{
required: false,
message: t('datasource.please_input_url'),
trigger: 'blur'
}
],
'configuration.port': [
{
required: true,
message: t('datasource.please_input_port'),
trigger: 'blur'
}
],
'configuration.initialPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.initial_pool_size'),
trigger: 'blur'
}
],
'configuration.minPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.min_pool_size'),
trigger: 'blur'
}
],
'configuration.maxPoolSize': [
{
required: true,
message: t('common.inputText') + t('datasource.max_pool_size'),
trigger: 'blur'
}
],
'configuration.queryTimeout': [
{
required: true,
message: t('common.inputText') + t('datasource.query_timeout'),
trigger: 'blur'
}
]
}
if (['oracle', 'sqlServer', 'pg', 'redshift', 'db2'].includes(form.value.type)) {
configRules['configuration.schema'] = [
{
required: true,
message: t('datasource.please_choose_schema'),
trigger: 'blur'
}
]
}
if (form.value.type === 'oracle') {
configRules['configuration.connectionType'] = [
{
required: true,
message: t('datasource.connection_mode'),
trigger: 'change'
}
]
}
rule.value = { ...cloneDeep(configRules), ...cloneDeep(defaultRule) }
}
watch(
() => form.value.type,
val => {
if (val !== 'API') {
rule.value = cloneDeep(defaultRule)
setRules()
}
},
{
immediate: true
}
)
const activeName = ref('table')
const showPriority = ref(false)
const submitForm = () => {
dsForm.value.clearValidate()
return dsForm.value.validate
}
const clearForm = () => {
return dsForm.value.clearValidate()
}
const resetForm = () => {
dsForm.value.resetFields()
}
const showSchema = ref(false)
const validatorSchema = () => {
dsForm.value.validateField('configuration.schema')
}
const activeParamsName = ref('')
const activeParamsID = ref(0)
const setActiveName = val => {
gridData.value = val.fields
activeParamsName.value = val.name
activeParamsName.value = val.serialNumber
}
const gridData = ref([])
defineExpose({
submitForm,
resetForm,
initForm,
clearForm
})
</script>
<template>
<div class="editor-detail">
<div class="detail-inner create-dialog">
<el-form
ref="dsForm"
:model="form"
:rules="rule"
label-width="180px"
label-position="top"
require-asterisk-position="right"
>
<el-form-item
:label="t('auth.datasource') + t('chart.name')"
prop="name"
v-show="activeStep !== 2"
>
<el-input
v-model="form.name"
autocomplete="off"
:placeholder="t('datasource.input_name')"
/>
</el-form-item>
<el-form-item :label="t('common.description')" v-show="activeStep !== 2">
<el-input
class="description-text"
type="textarea"
:placeholder="t('common.inputText')"
v-model="form.description"
:row="10"
:maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item
:label="t('datasource.host')"
prop="configuration.host"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
v-model="form.configuration.host"
:placeholder="t('datasource._ip_address')"
autocomplete="off"
/>
</el-form-item>
<el-form-item
:label="t('datasource.port')"
prop="configuration.port"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input-number
v-model="form.configuration.port"
autocomplete="off"
step-strictly
class="text-left"
:min="0"
:placeholder="t('common.inputText') + t('datasource.port')"
controls-position="right"
type="number"
/>
</el-form-item>
<el-form-item
:label="t('datasource.data_base')"
prop="configuration.dataBase"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
v-model="form.configuration.dataBase"
:placeholder="t('datasource.please_input_data_base')"
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.user_name')" v-if="form.type !== 'presto'">
<el-input
:placeholder="t('common.inputText') + t('datasource.user_name')"
v-model="form.configuration.username"
autocomplete="off"
/>
</el-form-item>
<el-form-item :label="t('datasource.password')" v-if="form.type !== 'presto'">
<CustomPassword
:placeholder="t('common.inputText') + t('datasource.password')"
show-password
type="password"
v-model="form.configuration.password"
/>
</el-form-item>
<el-form-item
:label="t('datasource.extra_params')"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input
:placeholder="t('common.inputText') + t('datasource.extra_params')"
v-model="form.configuration.extraParams"
autocomplete="off"
/>
</el-form-item>
<span
v-if="!['es', 'api'].includes(form.type)"
class="de-expand"
@click="showPriority = !showPriority"
>{{ t('datasource.priority') }}
<el-icon>
<Icon :name="showPriority ? 'icon_down_outlined' : 'icon_down_outlined-1'"></Icon>
</el-icon>
</span>
<template v-if="showPriority">
<el-row :gutter="24" class="mb16">
<el-col :span="12">
<el-form-item
:label="t('datasource.initial_pool_size')"
prop="configuration.initialPoolSize"
>
<el-input-number
v-model="form.configuration.initialPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.initial_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="t('datasource.min_pool_size')" prop="configuration.minPoolSize">
<el-input-number
v-model="form.configuration.minPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.min_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item :label="t('datasource.max_pool_size')" prop="configuration.maxPoolSize">
<el-input-number
v-model="form.configuration.maxPoolSize"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.max_pool_size')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="`${t('datasource.query_timeout')}(${t('common.second')})`"
prop="configuration.queryTimeout"
>
<el-input-number
v-model="form.configuration.queryTimeout"
controls-position="right"
autocomplete="off"
:placeholder="t('common.inputText') + t('datasource.query_timeout')"
type="number"
:min="0"
/>
</el-form-item>
</el-col>
</el-row>
</template>
</el-form>
</div>
</div>
</template>
<style lang="less" scoped>
.editor-detail {
width: 100%;
display: flex;
justify-content: center;
.ed-radio {
height: 22px;
}
.mb16 {
:deep(.ed-form-item) {
margin-bottom: 16px;
}
}
.execute-rate-cont {
border-radius: 4px;
margin-top: -8px;
}
.de-select {
width: 100%;
}
.ed-input-number {
width: 100%;
}
:deep(.is-controls-right > span) {
background: #fff;
}
.de-expand {
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: var(--ed-color-primary);
cursor: pointer;
display: inline-flex;
align-items: center;
.ed-icon {
margin-left: 4px;
}
}
:deep(.ed-date-editor.ed-input) {
.ed-input__wrapper {
width: 100%;
}
width: 100%;
}
.simple-cron {
height: 32px;
.ed-select,
.ed-input-number {
width: 140px;
margin: 0 8px;
}
}
.detail-inner {
width: 800px;
padding-top: 8px;
.description-text {
:deep(.ed-textarea__inner) {
height: 92px;
}
}
.base-info {
margin: 24px 0 16px 0;
}
.left-api_params {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border: 1px solid #bbbfc4;
width: 300px;
padding: 16px;
.name-copy {
display: none;
line-height: 24px;
margin-left: 4px;
}
.list-item_primary:hover {
.name-copy {
display: inline;
}
.label {
width: 74% !important;
}
}
}
.right-api_params {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border: 1px solid #bbbfc4;
border-left: none;
width: calc(100% - 200px);
}
.table-info-mr {
margin: 28px 0 12px 0;
.api-tabs {
:deep(.ed-tabs__nav-wrap::after) {
display: none;
}
}
}
.info-update {
height: 22px;
width: 100%;
display: flex;
align-items: center;
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
justify-content: center;
.update-info-line {
width: 208px;
height: 1px;
background: #bcbdbf;
margin: 0 8px;
}
.info-text,
.update-text {
padding-left: 16px;
position: relative;
color: #1f2329;
font-weight: 400;
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 14px;
font-style: normal;
line-height: 22px;
&::before {
width: 8px;
height: 8px;
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border: 1px solid var(--ed-color-primary);
border-radius: 50%;
}
&.active {
font-weight: 500;
}
&.active::before {
border: none;
background: var(--ed-color-primary);
}
}
}
.detail-operate {
text-align: right;
padding: 8px 0;
}
.flex-space {
display: flex;
align-items: center;
}
}
}
</style>

View File

@ -2,11 +2,11 @@
import { reactive, ref, computed, watch, nextTick } from 'vue'
import { ElIcon, ElMessage, ElMessageBox, ElMessageBoxOptions } from 'element-plus-secondary'
import CreatDsGroup from './CreatDsGroup.vue'
import { Icon } from '@/components/icon-custom'
import type { DsType } from './DsTypeList.vue'
import DsTypeList from './DsTypeList.vue'
import { useI18n } from '@/hooks/web/useI18n'
import EditorDetail from './EditorDetail.vue'
import EditorDetailPlugin from './EditorDetailPlugin.vue'
import ExcelDetail from './ExcelDetail.vue'
import { save, update, validate, latestUse, isShowFinishPage, checkRepeat } from '@/api/datasource'
import { Base64 } from 'js-base64'
@ -19,6 +19,9 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import FinishPage from '../FinishPage.vue'
import { cloneDeep } from 'lodash-es'
import { useCache } from '@/hooks/web/useCache'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { XpackComponent, PluginComponent } from '@/components/plugin'
interface Node {
name: string
id: string
@ -70,6 +73,7 @@ const filterText = ref('')
const currentDsType = ref('')
const emits = defineEmits(['refresh'])
const { emitter } = useEmitt()
const isPlugin = ref(false)
const selectDsType = (type: string) => {
currentDsType.value = type
activeStep.value = 1
@ -83,6 +87,7 @@ const selectDsType = (type: string) => {
.some(ele => {
if (ele.type === currentDsType.value) {
dsTree.value.setCurrentNode(ele)
isPlugin.value = ele['isPlugin']
return true
}
return false
@ -93,6 +98,7 @@ const selectDsType = (type: string) => {
const handleDsNodeClick = data => {
if (!data.type) return
selectDsType(data.type)
isPlugin.value = data['isPlugin']
}
const handleNodeClick = (data: Node) => {
currentType.value = data.type
@ -135,25 +141,33 @@ const getDatasourceTypes = () => {
})
}
getDatasourceTypes()
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
data.forEach(item => {
const { name, category, type, icon, extraParams, staticMap } = item
const node = {
name,
category,
catalog: category,
type,
icon,
extraParams,
isPlugin: true,
staticMap
}
const index = typeList.findIndex(ele => ele === node.catalog)
if (index !== -1) {
databaseList[index].push(node)
databaseList.value[index].push(node)
}
})
}
const getPluginStatic = type => {
const arr = pluginDs.value.filter(ele => {
return ele.type === type
})
return arr && arr.length > 0 ? arr[0].staticMap?.index : null
}
const getLatestUseTypes = () => {
latestUse({}).then(res => {
@ -663,7 +677,8 @@ defineExpose({
<template #default="{ node, data }">
<span class="custom-tree-node flex-align-center">
<el-icon v-if="!!data.catalog" class="icon-border" style="width: 18px; height: 18px">
<Icon :name="`${data.type}-ds`"></Icon>
<Icon v-if="data['isPlugin']" :static-content="data.icon"></Icon>
<Icon v-else :name="`${data.type}-ds`"></Icon>
</el-icon>
<span :title="node.label" class="label-tooltip">{{ node.label }}</span>
</span>
@ -687,8 +702,21 @@ defineExpose({
:form="form"
:editDs="editDs"
:active-step="activeApiStep"
v-if="activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible"
v-if="
activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible && !isPlugin
"
></editor-detail>
<plugin-component
:jsname="getPluginStatic(currentDsType)"
ref="detail"
:form="form"
:editDs="editDs"
:active-step="activeApiStep"
v-if="
activeStep !== 0 && currentDsType && currentDsType !== 'Excel' && visible && isPlugin
"
>
</plugin-component>
<template v-if="activeStep !== 0 && currentDsType == 'Excel'">
<excel-detail :editDs="editDs" ref="excel" :param="form2"></excel-detail>
</template>
@ -738,6 +766,11 @@ defineExpose({
:name="dsInfo.name"
v-if="showFinishPage"
></FinishPage>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</el-drawer>
<creat-ds-group
@ -745,10 +778,6 @@ defineExpose({
@finish="complete"
ref="creatDsFolder"
></creat-ds-group>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</template>
<style lang="less">

View File

@ -45,6 +45,7 @@ import { interactiveStoreWithOut } from '@/store/modules/interactive'
import treeSort from '@/utils/treeSortUtils'
import { useCache } from '@/hooks/web/useCache'
import { useEmbedded } from '@/store/modules/embedded'
import { XpackComponent } from '@/components/plugin'
const route = useRoute()
const interactiveStore = interactiveStoreWithOut()
interface Field {
@ -302,6 +303,26 @@ const showErrorInfo = info => {
dialogErrorInfo.value = true
}
const pluginDs = ref([])
const loadDsPlugin = data => {
pluginDs.value = data
}
const getDsIcon = data => {
if (pluginDs?.value.length === 0) return null
if (!data.leaf) return null
const arr = pluginDs.value.filter(ele => {
return ele.type === data.type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconType = type => {
const arr = pluginDs.value.filter(ele => {
return ele.type === type
})
return arr && arr.length > 0 ? arr[0].icon : null
}
const getDsIconName = data => {
if (!data.leaf) return 'dv-folder'
return `${data.type}-ds`
@ -945,7 +966,7 @@ const getMenuList = (val: boolean) => {
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-icon :class="data.leaf && 'icon-border'" style="font-size: 18px">
<Icon :name="getDsIconName(data)"></Icon>
<Icon :static-content="getDsIcon(data)" :name="getDsIconName(data)"></Icon>
</el-icon>
<span
:title="node.label"
@ -1004,7 +1025,10 @@ const getMenuList = (val: boolean) => {
<div class="datasource-info">
<div class="info-method">
<el-icon class="icon-border">
<Icon :name="`${nodeInfo.type}-ds`"></Icon>
<Icon
:static-content="getDsIconType(nodeInfo.type)"
:name="`${nodeInfo.type}-ds`"
></Icon>
</el-icon>
<span :title="nodeInfo.name" class="name ellipsis">
{{ nodeInfo.name }}
@ -1584,6 +1608,11 @@ const getMenuList = (val: boolean) => {
</span>
</template>
</el-dialog>
<XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="
@load-ds-plugin="loadDsPlugin"
/>
</div>
</template>

View File

@ -0,0 +1,16 @@
package io.dataease.extensions.datasource.dto;
import lombok.Data;
/**
* @Author Junjun
*/
@Data
public class DsTypeDTO {
private String type;
private String name;
private String catalog;
private String prefix;
private String suffix;
}

View File

@ -20,12 +20,16 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class ProviderFactory {
public static Provider getProvider(String type) {
public static Provider getProvider(String type) throws DEException {
List<String> list = Arrays.stream(DatasourceConfiguration.DatasourceType.values()).map(DatasourceConfiguration.DatasourceType::getType).toList();
if (list.contains(type)) {
return SpringContextUtil.getApplicationContext().getBean("calciteProvider", Provider.class);
}
return getInstance(type);
Provider instance = getInstance(type);
if (instance == null) {
DEException.throwException("插件异常,请检查插件");
}
return instance;
}
public static Provider getDefaultProvider() {

View File

@ -1,4 +1,4 @@
package io.dataease.datasource.provider;
package io.dataease.extensions.datasource.provider;
import java.sql.*;
import java.util.Properties;
@ -43,4 +43,4 @@ public class DriverShim implements Driver {
public Connection connect(String u, Properties p) throws SQLException {
return this.driver.connect(u, p);
}
}
}

View File

@ -1,4 +1,4 @@
package io.dataease.datasource.provider;
package io.dataease.extensions.datasource.provider;
import java.io.File;
@ -94,4 +94,4 @@ public class ExtendedJdbcClassLoader extends URLClassLoader {
throw new IOException("Error, could not add URL to system classloader");
}
}
}
}

View File

@ -14,6 +14,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -38,6 +39,22 @@ public abstract class Provider {
public abstract List<TableField> fetchTableField(DatasourceRequest datasourceRequest) throws DEException;
public abstract void hidePW(DatasourceDTO datasourceDTO);
public Statement getStatement(Connection connection, int queryTimeout) {
if (connection == null) {
DEException.throwException("Failed to get connection!");
}
Statement stat = null;
try {
stat = connection.createStatement();
stat.setQueryTimeout(queryTimeout);
} catch (Exception e) {
DEException.throwException(e.getMessage());
}
return stat;
}
public String rebuildSQL(String sql, SQLMeta sqlMeta, boolean crossDs, Map<Long, DatasourceSchemaDTO> dsMap) {
logger.info("calcite sql: " + sql);
if (crossDs) {

View File

@ -0,0 +1,54 @@
package io.dataease.extensions.datasource.vo;
import lombok.Data;
import java.util.List;
/**
* @Author Junjun
*/
@Data
public class PluginDatasourceType extends Configuration {
private List<String> illegalParameters;
private List<String> showTableSqls;
static public enum DatasourceType {
hive("hive", "Apache Hive", "DL", "`", "`");
private String type;
private String name;
private String catalog;
private String prefix;
private String suffix;
DatasourceType(String type, String name, String catalog, String prefix, String suffix) {
this.type = type;
this.name = name;
this.catalog = catalog;
this.prefix = prefix;
this.suffix = suffix;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
public String getCatalog() {
return catalog;
}
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
}
}