diff --git a/.eslintrc b/.eslintrc index 0a25792..1541dc7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,7 +30,8 @@ "no-use-before-define": [ "error", { - "functions": false + "functions": false, + "classes ": false } ], "new-cap": [ @@ -179,7 +180,7 @@ "array-bracket-spacing": ["error", "never"], // 数组紧贴括号部分不允许包含空格 "object-curly-spacing": ["error", "always"], // 对象紧贴花括号部分不允许包含空格 "no-regex-spaces": "error", // 禁止正则表达式字面量中出现多个空格 - "no-multi-spaces": "error", // 禁止出现多个空格而且不是用来作缩进的 + // "no-multi-spaces": "error", // 禁止出现多个空格而且不是用来作缩进的 "block-spacing": ["error", "never"], // 单行代码块中紧贴括号部分不允许包含空格 "computed-property-spacing": ["error", "never"], // 禁止括号和其内部值之间的空格 "no-trailing-spaces": [ diff --git a/README.md b/README.md index cca899a..ac03007 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## 开始 安装依赖 ``` -yarn +yarn install ``` 开始开发 @@ -15,8 +15,107 @@ yarn yarn dev ``` +## 决策平台开发: + +### A.项目运行 + +#### 1. 工程`decision-webui-dev`添加代理(可跳过) +```js + webpack/webpack.config + "/plugin/dcm": { + pathRewrite: { "^/plugin/dcm": "" }, + target: "http://localhost:10002", + }, +``` +#### 2. 工程`decision-webui-dev`引入 + fr环境:`templates/bundle.report.html` bi环境:`templates/bundle.bi.html` + +```html + // css 文件: + + + + + + // js 文件 + +``` + 若未设1,将`/plugin/dcm`替换成`http://localhost:10002`亦可 +#### 3. 启动工程[decision-webui-dev]以及数据连接[desicion-webui-dcm]工程 + +#### 4. 此时工程`decision-webui-dev`的`http://localhost:9002/#management/connnection`数据连接模块已替换成该工程 + +### B.插件形式添加数据连接-数据库 + +#### 1. 以多版本的tdsql为例 单一版本数据库不需drivers,versions,hasSchemas + +```js +BI.config("dec.connection.provider.datebase", function (provider) { + BI.isFunction(provider.registerJdbcDatabase) && provider.registerJdbcDatabase({ + text: 'TDSQL', // 数据库名称 + databaseType: 'tdsql', // 数据库key + driver: 'org.postgresql.Driver', // 默认驱动 + drivers: { + "pgsql": ["org.postgresql.Driver"], + "mysql": ["com.mysql.jdbc.Driver"] + }, // 驱动可选项,version: array[driver],[0]为该版本的默认驱动 + versions: ["pgsql", "mysql"], // array[version] + urls: { + "org.postgresql.Driver": "jdbc:postgresql://hostname:port/database?finedbType=tdsql-pgsql", + "com.mysql.jdbc.Driver": "jdbc:mysql://hostname:port/database?finedbType=tdsql-mysql" + }, // urlkey : url 一个驱动对应一个url + url: 'jdbc:postgresql://hostname:port/database?finedbType=tdsql-pgsql', + commonly: false, + internal: true, + type: 'jdbc', 数据库类型 + hasSchema: true, // 默认是否支持模式 + hasSchemas: { + "pgsql": true, + "mysql": false, + },是否支持模式 version: boolean + kerberos: false, // 是否添加kerberos认证方式 + }, function (url) { + var result = url.match(/^jdbc:(mysql|postgresql):\/\/([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?\/([0-9a-zA-Z_\\.]+)(.*)finedbType=([^&]+)(|(&.*))/i); // 匹配正则 + if (result) { + return { + host: result[2], //主机 + port: result[4] === "port" ? "" : result[4], // 端口 + databaseName: result[5], // 数据库名称 + version: result[7].split('-')[1] ?? "pgsql", // 版本 单版本不要返回这个 + }; + } +//适配原先tbase的url + result = url.match(/^jdbc:postgresql:\/\/([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?\/([0-9a-zA-Z_\\.]+)(.*)/i); + if (result) { + return { + host: result[1], + port: result[3] === "port" ? "" : result[3], + databaseName: result[4], + version: "pgsql", + }; + } + + }); + }); +``` + +### C 工程开发 + +#### 1. 图片资源添加 + 工程`decision-webui-dev` + decision-webui/dist/images/1x/icon/database + decision-webui/dist/images/2x/icon/database + +#### 2. 国际化添加 + 工程`decision-webui-dev` + decision-i18n/decision-main-i18n/src/main/resources/com/fr/decision/web/i18n + +#### 3. 版本控制 + 版本和平台保持一致 + ## 接口文档: -### 增加数据连接类型 +### A 增加数据连接类型 +#### 1. 增加数据连接类型 使用`BI.config`,ConstantName名称为`dec.constant.database.conf.connect.types`,值为连接的名称 例如增加`Redis`的连接: @@ -30,25 +129,25 @@ BI.config(ConstantPluginTyps, (datas: string[]) => [...datas, { }]); ``` -### 数据连接填写页面 +#### 2. 数据连接填写页面 edit属性值为填写组件shortcut的名称 -### 数据连接展示页面 +#### 3. 数据连接展示页面 show属性值为组件shortcut的名称 -### 示例 -``` +#### 4. 示例 +```js const DataBaseConfigProvider = 'dec.connection.provider.datebase'; const RedisShowName = 'dec.dcm.connection.plugin.redis.show'; const RedisEditName = 'dec.dcm.connection.plugin.redis.edit'; BI.config(DataBaseConfigProvider, function (provider) { - provider.registerDatabaseType({ + provider.registerDatabaseType([{ text: "Redis", databaseType: "Redis", edit: "dec.dcm.connection.plugin.demo.edit", show: "dec.dcm.connection.plugin.demo.show", - }); + }]); }); const RedisShow = BI.inherit(BI.Widget, { @@ -116,7 +215,7 @@ const RedisShow = BI.inherit(BI.Widget, { ], }, ], - }; + }, }, }); BI.shortcut(RedisShowName, RedisShow); @@ -211,7 +310,55 @@ const RedisEdit = BI.inherit(BI.Widget, { password: this.password.getValue(), }; }, + //可以触发组件的数据save方法,不需要则可不写 + async save() { + let result = false; + await Promise.resolve().then(() => {result = true}); + //要求返回是否成功的boolean变量 + return result; + }, }); BI.shortcut(RedisEditName, RedisEdit); ``` +### B 添加数据连接实例 +#### 1. 增加数据连接 +```js +BI.config('dec.constant.connection.list', function (value) { + const result = [{ + "connectionType": "Redis",//和databaseType一致 + "connectionName": "CHART",//类似于id,唯一性 + "pluginConnection": true,//表示是外来添加的插件 + "connectionData": { + //表单保存数据 + }, + "connectionId": null, + }]; + return value.concat(result); + }) +``` +### C 添加数据连接类型分类 +#### 1. 添加分类DEMO +```js +BI.config('dec.constant.database.filter.type', (value) => { + value.push({ + text:"DEMO", + value:"DEMO_VALUE" + }); + return value; + }); +``` +#### 2. 添加数据连接类型进DEMO +```js + BI.config('dec.connection.provider.datebase', function (provider) { + text: "Redis",//必填 + databaseType: "Redis",//唯一值 + marker: 'DEMO_VALUE',//marker对标dec.constant.database.filter.type常量item的value,用于过滤 + isHideConnection: true, //是否隐藏测试连接按钮 + isNoSave: true,//是否不执行平台的保存逻辑 + iconUrl:'https://work.fineres.com/secure/projectavatar?pid=10301&avatarId=10011', + driver: 'com.amazon.redshift.jdbc41.Driver', + drivers: ['com.amazon.redshift.jdbc4.Driver', 'com.amazon.redshift.jdbc41.Driver'], + url: 'jdbc:redshift://endpoint:port/database', + } +``` diff --git a/babel.config.js b/babel.config.js index 9085ac7..092e4d1 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,13 @@ -module.exports = function (api) { - return require("@fui/babel-preset-fineui").configs.base(api) +module.exports = api => { + const { plugins, presets, sourceType } = require("@fui/babel-preset-fineui").configs.base(api); + + return { + compact: false, + presets, + sourceType, + plugins: [ + ...plugins, + "@babel/plugin-proposal-logical-assignment-operators", + ], + }; }; diff --git a/i18n/zh_cn.properties b/i18n/zh_cn.properties index 30f185f..1cb2866 100644 --- a/i18n/zh_cn.properties +++ b/i18n/zh_cn.properties @@ -62,6 +62,11 @@ Dec-Dcm_Connection_Form_UserName= 用户名 Dec-Dcm_Connection_Form_Password= 密码 Dec-Dcm_Connection_Form_Principal= 客户端principal Dec-Dcm_Connection_Form_KeyPath= keytab密钥路径 +Dec-Dcm_Connection_Form_Krb5File= krb5.conf文件 +Dec-Dcm_Connection_File_Upload_Success= 已成功上传并校验成功 +Dec-Dcm_Connection_File_Upload_Error= 检测异常 +Dec-Dcm_Connection_File_Upload_ErrorCode= 错误代码 +Dec-Dcm_Connection_File_Upload_ErrorMsg= 错误详情 Dec-Dcm_Connection_Form_Pool_Properties= 连接池属性 Dec-Dcm_Connection_Form_SQL_Validation_Query= SQL验证查询 Dec-Dcm_Connection_Form_Connection-Check= 获取连接前校验 @@ -80,10 +85,10 @@ Dec-Dcm_Connection_Form_Database_Max_Wait= 最大等待时间 Dec-Dcm_Connection_Form_Database_Validation_Query= SQL验证查询 Dec-Dcm_Connection_Form_Database_Test_On_Borrow= 获取连接前检验 Dec-Dcm_Connection_Form_Database_Test_On_Return= 归还连接前检验 -Dec-Dcm_Connection_Form_Database_Test_While_Idle= 开启空闲回收器检验 -Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis= 空闲连接回收器休眠时间 +Dec-Dcm_Connection_Form_Database_Test_While_Idle= 获取连接时空闲连接可用性校验 +Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis= 空闲连接回收器工作间隔 Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num= 空闲连接回收检查数 -Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis= 保持空闲最小时间值 +Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis= 空闲连接回收时间阈值 Dec-Dcm_Connection_Make_Sure_Delete= 确定删除该数据连接? Dec-Dcm_Connection_ReConnect= 重新连接 Dec-Dcm_Connection_JNDI_Form_ConnectionName= JNDI的名字 @@ -96,7 +101,7 @@ Dec-Dcm_Connection_JNDI= JNDI数据连接 Dec-Dcm_Connection_JNDI_Warning= 注意:需要把包含INTIAL_CONTEXT_FACTORY类的.jar文件复制到软件安装目录下的/lib目录下 Dec-Dcm_Connection_Error= 接口访问错误 Dec-Dcm_Connection_Is_Using= 该连接正在被{R1}编辑,请稍后再试 -Dec-Dcm_Connection_Check_Integer= 请输入不小于0的整数 +Dec-Dcm_Connection_Check_Integer= 请输入不小于{}的整数 Dec-Dcm_Connection_Check_Number= 只允许为整数 Dec-Dcm_Connection_JDBC_Other=其他JDBC Dec-Dcm_Connection_JDBC_Warning= 请确认已经将krb5.Conf文件添加到/webapps/webroot/WEB_INF/resources目录 @@ -303,4 +308,9 @@ BI-Basic_No_Select= 不选 BI-Basic_Now= 此刻 Dec-Dcm_Connection_Analytic_DB=阿里云AnalyticDB Dec-Dcm_Connection_Value_Out_Range=数值超出范围 -Dec-Dcm_Socket_Unable_Connect_Tip=可能出现编辑冲突 \ No newline at end of file +Dec-Dcm_Socket_Unable_Connect_Tip=可能出现编辑冲突 +Dec-Dcm_Connection_File_Upload_ErrorTip1= 参考 +Dec-Dcm_Connection_File_Upload_ErrorTip2= kerberos配置 +Dec-Dcm_Connection_File_Upload_ErrorTip3= 获取帮助或联系技术支持 +Dec-Dcm_Connection_Timeout_Detection=数据连接超时检测 +Dec-Dcm_Connection_Timeout_Millisecond=毫秒(ms) \ No newline at end of file diff --git a/package.json b/package.json index d7f4eac..e59b383 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "author": "decision", "license": "MIT", "dependencies": { + "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", "@types/jss": "9.5.8", "autoprefixer": "^9.6.1", "es6-promise": "4.2.6", @@ -18,9 +19,9 @@ "devDependencies": { "@fui/babel-preset-fineui": "^1.0.0", "@types/jest": "24.0.11", - "@typescript-eslint/eslint-plugin": "1.7.0", - "@typescript-eslint/parser": "1.7.0", - "axios": "0.18.0", + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "axios": "^0.24.0", "babel-loader": "8.0.6", "body-parser": "1.18.3", "chokidar": "2.1.5", diff --git a/private/i18n.ts b/private/i18n.ts index 8f00670..eaf7e10 100644 --- a/private/i18n.ts +++ b/private/i18n.ts @@ -61,6 +61,11 @@ export default { 'Dec-Dcm_Connection_Form_Password': '密码', 'Dec-Dcm_Connection_Form_Principal': '客户端principal', 'Dec-Dcm_Connection_Form_KeyPath': 'keytab密钥路径', + 'Dec-Dcm_Connection_Form_Krb5File': 'krb5.conf文件', + 'Dec-Dcm_Connection_File_Upload_Success': ' 已成功上传并校验成功', + 'Dec-Dcm_Connection_File_Upload_Error': '检测异常', + 'Dec-Dcm_Connection_File_Upload_ErrorCode': '错误代码', + 'Dec-Dcm_Connection_File_Upload_ErrorMsg': '错误详情', 'Dec-Dcm_Connection_Form_Pool_Properties': '连接池属性', 'Dec-Dcm_Connection_Form_SQL_Validation_Query': 'SQL验证查询', 'Dec-Dcm_Connection_Form_Connection-Check': '获取连接前校验', @@ -79,10 +84,10 @@ export default { 'Dec-Dcm_Connection_Form_Database_Validation_Query': 'SQL验证查询', 'Dec-Dcm_Connection_Form_Database_Test_On_Borrow': '获取连接前检验', 'Dec-Dcm_Connection_Form_Database_Test_On_Return': '归还连接前检验', - 'Dec-Dcm_Connection_Form_Database_Test_While_Idle': '开启空闲回收器检验', - 'Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis': '空闲连接回收器休眠时间', + 'Dec-Dcm_Connection_Form_Database_Test_While_Idle': '获取连接时空闲连接可用性校验', + 'Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis': '空闲连接回收器工作间隔', 'Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num': '空闲连接回收检查数', - 'Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis': '保持空闲最小时间值', + 'Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis': '空闲连接回收时间阈值', 'Dec-Dcm_Connection_Make_Sure_Delete': '确定删除该数据连接?', 'Dec-Dcm_Connection_ReConnect': '重新连接', 'Dec-Dcm_Connection_JNDI_Form_ConnectionName': 'JNDI的名字', @@ -95,7 +100,7 @@ export default { 'Dec-Dcm_Connection_JNDI_Warning': '注意:需要把包含INTIAL_CONTEXT_FACTORY类的.jar文件复制到软件安装目录下的/lib目录下', 'Dec-Dcm_Connection_Error': '接口访问错误', 'Dec-Dcm_Connection_Is_Using': '该连接正在被{R1}编辑,请稍后再试', - 'Dec-Dcm_Connection_Check_Integer': '请输入不小于0的整数', + 'Dec-Dcm_Connection_Check_Integer': '请输入不小于{}的整数', 'Dec-Dcm_Connection_Check_Number': '只允许为整数', 'Dec-Dcm_Connection_JDBC_Other': '其他JDBC', 'Dec-Dcm_Connection_JDBC_Warning': '请确认已经将krb5.Conf文件添加到/webapps/webroot/WEB_INF/resources目录', @@ -306,4 +311,7 @@ export default { 'Dec-Connection_Lic_Limit_Approach_Tip': '当前数据连接数量超过注册lic限制({}个),所有数据连接都不可用,请删除多余的数据连接', 'Dec-Connection_Lic_Limit_Approach_Prevent_Tip': '当前数据连接数量已经达到注册lic限制({}个),无法新增', 'Dec-Dcm_Connection_Check_Fetch_Size_Range': '请输入0-1000000之间的值', + 'Dec-Dcm_Connection_File_Upload_ErrorTip1':'参考', + 'Dec-Dcm_Connection_File_Upload_ErrorTip2': 'kerberos配置', + 'Dec-Dcm_Connection_File_Upload_ErrorTip3': '取帮助或联系技术支持', }; diff --git a/src/modules/app.model.ts b/src/modules/app.model.ts index 200800c..01b74b5 100644 --- a/src/modules/app.model.ts +++ b/src/modules/app.model.ts @@ -7,7 +7,7 @@ import { getAllDatabaseTypes } from './app.service'; export class AppModel extends Model { static xtype = 'dec.dcm.model.main'; - childContext = ['pageIndex', 'datebaseTypeSelected', 'datebaseTypeSelectedOne', 'filter', 'connections', 'connectionSelected', 'connectionSelectedOne', 'saveEvent', 'testEvent', 'isCopy', 'connectionLicInfo']; + childContext = ['pageIndex', 'datebaseTypeSelected', 'datebaseTypeSelectedOne', 'filter', 'connections', 'connectionSelected', 'connectionSelectedOne', 'saveEvent', 'testEvent', 'isCopy', 'connectionLicInfo', 'noTestConnection']; state() { return { @@ -23,6 +23,7 @@ export class AppModel extends Model { currentConnectionNum: 0, maxConnectionNum: 0, }, + noTestConnection: false, }; } diff --git a/src/modules/app.provider.ts b/src/modules/app.provider.ts index af2db61..3662b4a 100644 --- a/src/modules/app.provider.ts +++ b/src/modules/app.provider.ts @@ -1,11 +1,67 @@ import { CONSTANT_PLUGIN_TYPES } from './app.constant'; +import { DATA_BASE_TYPES } from '@constants/constant'; -BI.provider('dec.connection.provider.datebase', function() { +BI.provider('dec.connection.provider.datebase', function () { this.resolves = {}; + function starRocksResolve(url: string) { + // 处理starRocks数据连接常规模式 + let result = url.match(/^jdbc:mysql:\/\/([0-9a-zA-Z_\\.-]+):([0-9a-zA-Z_\\.-]+)\/([0-9a-zA-Z_\\.-]+)\.([^]+)(.*)/i); + if (result) { + return { + host: result[1], + port: result[2] === 'port' ? '' : result[2], + catalog: result[3], + databaseName: result[4], + urlInfo: result[0], + }; + } else { + // 兼容老数据库里面没有catalog的情况 + result = url.match(/^jdbc:mysql:\/\/([0-9a-zA-Z_\\.-]+):([0-9a-zA-Z_\\.-]+)\/([^]+)(.*)/i); + if (result) { + return { + host: result[1], + port: result[2] === 'port' ? '' : result[2], + catalog: '', + databaseName: result[3], + urlInfo: result[0], + }; + } + } + // 处理starRocks数据连接负载均衡模式 + let loadBalance = url.match(/^jdbc:mysql:loadbalance:\/\/[^/]+\/([^/]+)\.([^/]+)/i); + if (loadBalance) { + return { + host: '', + port: '', + catalog: loadBalance[1], + databaseName: loadBalance[2], + urlInfo: loadBalance[0], + } + } else { + // 兼容老数据库里面没有catalog的情况 + loadBalance = url.match(/^jdbc:mysql:loadbalance:\/\/[^/]+\/([^/]+)([^/]+)/i); + if (loadBalance) { + return { + host: '', + port: '', + catalog: '', + databaseName: loadBalance[1], + urlInfo: loadBalance[0], + } + } + } + return { + host: '', + port: '', + databaseName: '', + urlInfo: '', + }; + } // 原service中resolveUrlInfo方法 - function jdbcResolve (url: string) { + function jdbcResolve(url: string) { if (BI.isNull(url)) return {}; + const oracleUlr = url.match(/^jdbc:(oracle):(thin:([0-9a-zA-Z/]*)?@|thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(:|\/)([^]+)(.*)/i); if (oracleUlr) { return { @@ -15,8 +71,8 @@ BI.provider('dec.connection.provider.datebase', function() { urlInfo: oracleUlr[10], }; } - - const greenplumUrl = url.match(/^jdbc:(pivotal:greenplum):(thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(\/|;)([^]+)(.*)/i); + + const greenplumUrl = url.match(/^jdbc:(pivotal:greenplum):(thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(\/|;DatabaseName=)?([^]+)(.*)/i); if (greenplumUrl) { return { host: greenplumUrl[4], @@ -25,7 +81,7 @@ BI.provider('dec.connection.provider.datebase', function() { urlInfo: greenplumUrl[9], }; } - const result = url.match(/^jdbc:(mysql|sqlserver|db2|impala|kylin|phoenix|derby|gbase|gbasedbt-sqli|informix-sqli|h2|postgresql|hive2|vertica|kingbase|presto|redshift|postgresql|clickhouse):(thin:([0-9a-zA-Z/]*)?@|thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(\/|;DatabaseName=)?([^]+)?(.*)/i); + const result = url.match(/^jdbc:(mysql|sqlserver|db2|dm|impala|kylin|phoenix|derby|gbase|gbasedbt-sqli|informix-sqli|h2|postgresql|hive2|vertica|kingbase|presto|redshift|postgresql|clickhouse|trino|sybase:Tds):(thin:([0-9a-zA-Z/]*)?@|thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(\/|;DatabaseName=)?([^]+)?(.*)/i); if (result) { return { host: result[5], @@ -34,13 +90,24 @@ BI.provider('dec.connection.provider.datebase', function() { urlInfo: result[10], }; } - + + // 处理SAP HANA数据连接url + const sapHanaUrl = url.match(/^jdbc:(sap):(thin:([0-9a-zA-Z/]*)?@|thin:([0-9a-zA-Z/]*)?@\/\/|\/\/|)([0-9a-zA-Z_\\.-]+)(:([0-9|port]+))?(\?databaseName=)?([^&]+)([^]+)?(.*)/i); + if (sapHanaUrl) { + return { + host: sapHanaUrl[5], + port: sapHanaUrl[7] === 'port' ? '' : sapHanaUrl[7], + databaseName: sapHanaUrl[9] || '', + urlInfo: sapHanaUrl[10], + }; + } + // 处理oracle的RAC方式 if (/^jdbc:oracle:thin:([0-9a-zA-Z/]*)?@\(DESCRIPTION/i.test(url)) { const host = url.match(/\(HOST\s*=\s*([0-9a-zA-Z_\\.-]+)\)/i); const port = url.match(/\(PORT\s*=\s*([0-9]+)\)/i); const databaseName = url.match(/\(SERVICE_NAME\s*=\s*([\s0-9a-zA-Z_\\.]+)\)/i); - + return { host: host ? host[1] : '', port: port && port[1] !== 'port' ? port[1] : '', @@ -48,7 +115,6 @@ BI.provider('dec.connection.provider.datebase', function() { urlInfo: '', }; } - return { host: '', port: '', @@ -57,18 +123,55 @@ BI.provider('dec.connection.provider.datebase', function() { }; } + function coverBaseDatabase(config) { + const baseDataBase = DATA_BASE_TYPES.find(item => item.text === config.text); + if (BI.isNotNull(baseDataBase)) { + // 覆盖基础配置 + Object.assign(baseDataBase, config); + + return true; + } + + return false; + } + + function filterPluginDataTypeByPriority() { + const originTypes = [...BI.Constants.getConstant(CONSTANT_PLUGIN_TYPES)]; + const sortDataTypes = BI.sortBy(originTypes, (index, value: any) => { + return value.priority || 0; + }) + return BI.uniqWith(sortDataTypes, (current, other) => { + return current.text == other.text; + }); + } + this.registerDatabaseType = (config: any) => { + if (coverBaseDatabase(config)) return; + BI.config(CONSTANT_PLUGIN_TYPES, connections => BI.concat(connections, config)); }; - + this.registerJdbcDatabase = (config: any, resolve?: Function) => { - BI.config(CONSTANT_PLUGIN_TYPES, connections => BI.concat(connections, { ...config, type: 'jdbc' })); - + config = { + ...config, + type: 'jdbc', + }; BI.isFunction(resolve) && (this.resolves[config.databaseType] = resolve); + + if (coverBaseDatabase(config)) return; + + BI.config(CONSTANT_PLUGIN_TYPES, connections => BI.concat(connections, config)); }; this.$get = () => BI.inherit(BI.OB, { - getJdbcResolveByType: (type: string) => this.resolves[type] || jdbcResolve, + getJdbcResolveByType: (type: string) => { + // starRocks特殊处理 + // todo: 后面有专门的迭代系统处理,这里先临时解决下bug + if (type === "starrocks"){ + return starRocksResolve + } + return this.resolves[type] || jdbcResolve + }, customDatabaseType: BI.Constants.getConstant(CONSTANT_PLUGIN_TYPES), }); }); diff --git a/src/modules/app.service.ts b/src/modules/app.service.ts index 145b807..b236bb2 100644 --- a/src/modules/app.service.ts +++ b/src/modules/app.service.ts @@ -1,7 +1,13 @@ -import { DATA_BASE_TYPES, DATA_BASE_TYPES_OTHER, DESIGN_DRIVER_TYPE, OTHER_JDBC } from '@constants/constant'; +import { + DATA_BASE_TYPES, + DATA_BASE_TYPES_OTHER, + DESIGN_DRIVER_TYPE, + OTHER_JDBC, + DATABASE_TYPE, +} from '@constants/constant'; import { DatabaseType } from './app.typings'; import { Connection } from './crud/crud.typings'; -export function getAllDatabaseTypes():DatabaseType[] { +export function getAllDatabaseTypes(): DatabaseType[] { return [ ...DATA_BASE_TYPES, ...BI.Providers.getProvider('dec.connection.provider.datebase').customDatabaseType.map(item => { @@ -45,13 +51,13 @@ export function getJdbcDatabaseType(database: string, driver: string): DatabaseT if (!databaseType) { return DATA_BASE_TYPES_OTHER; } - + return databaseType; } -export function resolveUrlInfo (url: string, database?: string) { +export function resolveUrlInfo(url: string, database?: string) { if (BI.isNull(url)) return {}; - + return BI.Providers.getProvider('dec.connection.provider.datebase').getJdbcResolveByType(database)(url) || { host: '', port: '', @@ -61,14 +67,40 @@ export function resolveUrlInfo (url: string, database?: string) { } // 拼接url -export function splitUrl(host: string, port: string, database: string, baseUrl: string) { +export function splitUrl(host: string, port: string, catalog: string, database: string, baseUrl: string, databaseType: string) { if (baseUrl.startsWith('jdbc:sqlserver')) { return baseUrl.replace('hostname', host).replace(':port', port ? `:${port}` : '') .replace('=database', `=${database}`); } - + + // https://work.fineres.com/browse/REPORT-72078 + if (baseUrl.startsWith('jdbc:informix-sqli')) { + return baseUrl.replace('hostname', host) + .replace(':port', port ? `:${port}` : '') + .replace('database', database) + .replace(':INFORMIXSERVER={server}', ''); + } + + if (databaseType === DATABASE_TYPE.STAR_ROCKS) { + let databaseStr = ''; + if (!catalog || !database) { + databaseStr = catalog + database; + } else { + databaseStr = catalog + '.' + database; + } + return baseUrl.replace('hostname', host).replace(':port', port ? `:${port}` : '') + .replace('default_catalog.database', databaseStr); + } + + if (databaseType === DATABASE_TYPE.SAP_HANA) { + return baseUrl.replace('hostname', host) + .replace('port', port || '') + .replace('=database', `=${database}`); + } + return baseUrl.replace('hostname', host).replace(':port', port ? `:${port}` : '') - .replace('database', database) + .replace('/database', `/${database}`) + .replace(':database', `:${database}`) .replace('dbname', database); } @@ -77,13 +109,13 @@ export function connectionCanEdit(connection: Connection) { // privilegeType === 4 代表编辑权限,privilegeValue === 2 代表有权限 return connection.privilegeDetailBeanList.some(item => item.privilegeType === 4 && item.privilegeValue === 2); } - + return true; } export function getTextByDatabaseType(databaseType: string) { const database = getAllDatabaseTypes().find(item => item.databaseType === databaseType); - + return database ? database.text : ''; } diff --git a/src/modules/app.ts b/src/modules/app.ts index c347927..46cd7b7 100644 --- a/src/modules/app.ts +++ b/src/modules/app.ts @@ -7,8 +7,10 @@ import { Tab } from '@fui/core'; import { Datebase } from './pages/database/database'; import { Maintain } from './pages/maintain/maintain'; import { ConnectionPool } from './pages/connection_pool/connection_pool'; +import { TimeOutSetting } from './pages/setting/setting'; import './app.provider'; import '../less/index.less'; +import "./pages/__point__/connect.point"; @shortcut() @store(AppModel) @@ -37,7 +39,12 @@ export class App extends BI.Widget { { el: { type: Title.xtype, - cls: 'bi-border-bottom', + listeners: [{ + eventName: 'EVENT_CLICK_SETTING', + action:() => { + this._setting(); + }, + }] }, height: 40, }, @@ -75,13 +82,30 @@ export class App extends BI.Widget { } }, }, - left: 10, - top: 10, - right: 10, - bottom: 10, + left: 0, + top: 0, + right: 0, + bottom: 0, }], }, ], }; } + + private _setting() { + const settingLayerId = BI.UUID(); + BI.Layers.create(settingLayerId, null, { + container: this, + render: { + type: TimeOutSetting.xtype, + listeners: [{ + eventName: "EVENT_CHANGE", + action: function () { + BI.Layers.remove(settingLayerId); + }, + }], + }, + }, this); + BI.Layers.show(settingLayerId); + } } diff --git a/src/modules/app.typings.d.ts b/src/modules/app.typings.d.ts index 67a5665..3eda6f3 100644 --- a/src/modules/app.typings.d.ts +++ b/src/modules/app.typings.d.ts @@ -8,9 +8,18 @@ export interface DatabaseType { internal: boolean; type: string; hasSchema?: boolean; + hasSchemas?: { + [key: string]: boolean; + }; + fetchSize: number; + versionConfig?: { + [key: string]: DatabaseType; + }, kerberos?: boolean; iconUrl?: string; + versions?: string[]; urls?: { [key: string]: string; - } + }; + marker?: string; } diff --git a/src/modules/components/collapse/collapse.ts b/src/modules/components/collapse/collapse.ts index 58e8db1..de2c51d 100644 --- a/src/modules/components/collapse/collapse.ts +++ b/src/modules/components/collapse/collapse.ts @@ -12,6 +12,7 @@ export class Collapse extends BI.BasicButton { name: '', isCollapse: true, $testId: 'dec-dcm-components-collapse', + el: {}, } rightFont: IconLabel; @@ -57,6 +58,7 @@ export class Collapse extends BI.BasicButton { lgap: 2, text: this.options.name, }, + this.options.el, ], }; } @@ -64,4 +66,8 @@ export class Collapse extends BI.BasicButton { doClick() { this.store.setCollapse(!this.model.isCollapse); } + + setCollapse(v: boolean) { + this.store.setCollapse(v); + } } diff --git a/src/modules/components/file_chooser/file_chooser.model.ts b/src/modules/components/file_chooser/file_chooser.model.ts new file mode 100644 index 0000000..708bd0f --- /dev/null +++ b/src/modules/components/file_chooser/file_chooser.model.ts @@ -0,0 +1,68 @@ +import { model, Model } from '@core/core'; + +type RootInfo = { + url: string; // api url + prefix: string; // 路径前缀 + root: string; // 根文件夹名称 +}; + +export const ROOT_INFO_MAP: Record = { + // 证书 resources/certificates/ + certificates: { + url: '/v10/certificates/all', + prefix: 'resources/', + root: 'certificates', + }, +}; + +@model() +export class FileChooserModel extends Model { + static xtype = 'dec.dcm.model.components.file_chooser'; + + private options: { + root: string; + }; + + state() { + return { + keyword: '', // 搜索关键字 + items: [], // 文件项 + }; + } + + actions = { + /** + * 请求获取items + * @param callback 回调 + */ + requestGetItems: (callback?: Function) => { + const { keyword } = this.model; + const { url, prefix, root } = ROOT_INFO_MAP[this.options.root]; + const requestUrl = `${url}?keyword=${encodeURIComponent(keyword)}`; + Dec.reqGetHandle(requestUrl, '', (data) => { + this.model.items = data + .concat({ + id: root, + text: prefix + root, + value: prefix + root, + isParent: true, + }) + .map((item) => ({ + ...item, + value: prefix + item.path, + open: item.id === root || BI.isKey(keyword), + })); + BI.isFunction(callback) && callback(); + }); + }, + + /** + * 设置keyword + * @param value + */ + setKeyword: (value: string) => { + this.model.keyword = value; + this.requestGetItems(); + }, + }; +} diff --git a/src/modules/components/file_chooser/file_chooser.ts b/src/modules/components/file_chooser/file_chooser.ts new file mode 100644 index 0000000..dc430d1 --- /dev/null +++ b/src/modules/components/file_chooser/file_chooser.ts @@ -0,0 +1,183 @@ +import { EVENT_CHANGE } from './../collapse/collapse'; +import { shortcut, store } from '@core/core'; +import { SignEditor, MultiLayerSingleLevelTree, SearchEditor, Button, Editor } from '@fui/core'; +import { FileChooserModel } from './file_chooser.model'; + +@shortcut() +@store(FileChooserModel, { + props(this: FileChooser) { + return this.options; + }, +}) +export class FileChooser extends BI.Widget { + static xtype = 'dec.dcm.components.file_chooser'; + + props = { + width: 300, + root: '', // 含义见model中的RootInfo + watermark: '', + value: '', + }; + + model: FileChooserModel['model']; + store: FileChooserModel['store']; + watch = { + items: (value) => { + this.fileTree.populate(value); + }, + }; + + textEditor: SignEditor; + keywordEditor: SearchEditor; + fileTree: MultiLayerSingleLevelTree; + sureButton: Button; + + render() { + const { width, watermark, value } = this.options; + + return { + type: BI.VerticalAdaptLayout.xtype, + height: 24, + items: [ + { + type: BI.SignEditor.xtype, + cls: 'bi-border-bottom bi-focus-shadow', + width, + height: 22, + watermark, + title: value, + value, + ref: (_ref: SignEditor) => { + this.textEditor = _ref; + }, + listeners: [ + { + eventName: BI.SignEditor.EVENT_CHANGE, + action: () => { + const value = this.textEditor.getValue(); + this.setValue(value); + }, + }, + ], + }, + { + el: { + type: BI.Button.xtype, + + text: BI.i18nText('Dec-Basic_Choose_File'), + clear: true, + handler: () => { + this.openFileChoosePopover(); + }, + }, + lgap: 10, + }, + ], + }; + } + + getValue(): string { + return this.textEditor.getValue(); + } + + setValue(value: string) { + this.options.value = value; + this.textEditor.text.setTitle(value); + this.textEditor.setValue(value); + } + + /** + * 打开文件选择弹窗 + */ + private openFileChoosePopover() { + // 重置搜索关键词 + this.store.setKeyword(''); + // 创建并显示窗口 + const popoverName = BI.UUID(); + BI.Popovers.create( + popoverName, + { + header: BI.i18nText('Dec-Data_Set_File_Select_Server_File'), + body: { + type: BI.VTapeLayout.xtype, + items: [ + { + type: BI.SearchEditor.xtype, + ref: (ref: SearchEditor) => { + this.keywordEditor = ref; + }, + height: 24, + value: this.model.keyword, + listeners: [ + { + eventName: BI.SearchEditor.EVENT_CHANGE, + action: () => { + const value = this.keywordEditor.getValue(); + this.store.setKeyword(value); + }, + }, + { + eventName: BI.SearchEditor.EVENT_CLEAR, + action: () => { + this.store.setKeyword(''); + }, + }, + ], + }, + { + el: { + type: BI.MultiLayerSingleLevelTree.xtype, + ref: (ref: MultiLayerSingleLevelTree) => { + this.fileTree = ref; + }, + keywordGetter: () => this.model.keyword, + items: this.model.items, + listeners: [ + { + eventName: BI.MultiLayerSingleLevelTree.EVENT_CHANGE, + action: () => { + this.sureButton.setEnable(true); + }, + }, + ], + }, + tgap: 15, + }, + { + type: BI.RightVerticalAdaptLayout.xtype, + height: 24, + vgap: 10, + items: [ + { + type: BI.Button.xtype, + text: BI.i18nText('BI-Basic_Cancel'), + level: 'ignore', + handler: () => { + BI.Popovers.remove(popoverName); + }, + }, + { + el: { + type: BI.Button.xtype, + ref: (ref: Button) => { + this.sureButton = ref; + }, + text: BI.i18nText('BI-Basic_OK'), + disabled: true, + handler: () => { + const value = this.fileTree.getValue()[0]; + this.setValue(value); + BI.Popovers.remove(popoverName); + }, + }, + lgap: 10, + }, + ], + }, + ], + }, + }, + this + ).show(popoverName); + } +} diff --git a/src/modules/components/file_upload/file_upload.less b/src/modules/components/file_upload/file_upload.less new file mode 100644 index 0000000..83c51ce --- /dev/null +++ b/src/modules/components/file_upload/file_upload.less @@ -0,0 +1,13 @@ +.data-conf-file { + .x-icon{ + width: 48px; + height: 48px; + } +} + +.data-keytab-file { + .x-icon{ + width: 48px; + height: 48px; + } +} diff --git a/src/modules/components/file_upload/file_upload.model.ts b/src/modules/components/file_upload/file_upload.model.ts new file mode 100644 index 0000000..bf6d905 --- /dev/null +++ b/src/modules/components/file_upload/file_upload.model.ts @@ -0,0 +1,35 @@ +import { model, Model } from '@core/core'; + +type UploadParam = { + keytabPath: string; + krb5ConfPath: string; + principal: string; +} + +@model() +export class FileUploadModel extends Model { + static xtype = 'dec.dcm.model.components.file_upload'; + + private options: { + inter: string; + } + + state() { + return { + uploadUrl: '', + fileName: '', + }; + } + + actions = { + setFileInfo: (params: UploadParam) => { + const inter = this.options.inter; + + this.model.uploadUrl = Dec.Utils.getEncodeURL(Dec.fineServletURL + inter, "", params); + }, + + setFileName:(v: string) => { + this.model.fileName = v; + } + } +} diff --git a/src/modules/components/file_upload/file_upload.ts b/src/modules/components/file_upload/file_upload.ts new file mode 100644 index 0000000..9a4f62f --- /dev/null +++ b/src/modules/components/file_upload/file_upload.ts @@ -0,0 +1,327 @@ +import { shortcut, store } from '@core/core'; +import { SignEditor, MultiLayerSingleLevelTree, SearchEditor, Button, Editor, Label } from '@fui/core'; +import { FileUploadModel } from './file_upload.model'; +import { ApiFactory } from 'src/modules/crud/apiFactory'; +import './file_upload.less'; + +const api = new ApiFactory().create(); + +@shortcut() +@store(FileUploadModel, { + props(this: FileUpload) { + return this.options; + }, +}) +export class FileUpload extends BI.Widget { + public static xtype = "dec.dcm.components.file_upload"; + public static EVENT_CHECK_SUCCESS = 'EVENT_CHECK_SUCCESS'; + public static EVENT_CLEAR_FILE = 'EVENT_CLEAR_FILE'; + + public props = { + watermark: '', + value: '', + processId: '', + disabled: false, + inter: '', + access: '', + iconCls: '' + }; + + model: FileUploadModel['model']; + store: FileUploadModel['store']; + watch = { + uploadUrl: function () { + this.uploader.setUrl(this.model.uploadUrl); + }, + }; + + public textEditor: SignEditor; + public keywordEditor: SearchEditor; + public fileTree: MultiLayerSingleLevelTree; + public sureButton: Button; + public infoLabel: Label; + + public render() { + const { width, watermark, value, processId, inter } = this.options; + let self = this; + const processName = BI.concat("process-", processId); + const processClass = BI.concat(".process-", processId); + this.setFileInfo({ + keytabPath: '', + principal:'', + krb5ConfPath: '', + }); + + return { + type: BI.VerticalLayout.xtype, + items:[{ + type: BI.FloatLeftLayout.xtype, + items: [ + { + type: BI.SignEditor.xtype, + cls: 'bi-border-bottom bi-focus-shadow', + width: 300, + height: 22, + watermark, + disabled: true, + value, + ref: (_ref: SignEditor) => { + this.textEditor = _ref; + }, + listeners: [ + { + eventName: BI.SignEditor.EVENT_CHANGE, + action: () => { + const value = this.textEditor.getValue(); + this.setValue(value); + }, + }, + ], + }, { + type: BI.Button.xtype, + width: 100, + _lgap: 10, + iconCls: "upload-font", + level: "ignore", + ghost: true, + ref: (_ref: Button) => { + this.uploadButton = _ref; + }, + text: BI.i18nText('Dec-Basic_Choose_File'), + handler: () => { + this.uploader.select(); + }, + }, + ], + }, { + type: BI.FloatLeftLayout.xtype, + tgap: 8, + invisible: true, + ref: (_ref) => { + this.fileInfo = _ref; + }, + items: [ + { + type: BI.VerticalLayout.xtype, + cls: "bi-border", + items: [{ + type: BI.HTapeLayout.xtype, + height: 68, + width: 300, + items: [{ + el: { + type: BI.IconLabel.xtype, + cls: this.options.iconCls, + }, + width: 48, + lgap: 8, + }, { + el: { + type: BI.VerticalLayout.xtype, + items : [{ + type: BI.Label.xtype, + width: 200, + height: 20, + textAlign: "left", + ref: (_ref: Label) => { + this.fileName = _ref; + }, + },{ + type: BI.VerticalAdaptLayout.xtype, + items: [{ + type: BI.Label.xtype, + cls: "bi-tips", + height: 20, + rgap: 3, + ref: (_ref: Label) => { + this.fileSize = _ref; + }, + }, { + type: BI.Label.xtype, + cls: "bi-tips", + height: 20, + ref: (_ref: Label) => { + this.fileModified = _ref; + }, + }] + + }], + + }, + tgap: 14, + lgap: 4, + }, { + el: { + type: BI.IconButton.xtype, + cls: "default-delete-font", + handler: function () { + NProgress.set(0.0); + self.xhr.abort(); + self.store.setFileName(''); + self.clearInfo(); + self.fireEvent(FileUpload.EVENT_CLEAR_FILE); + + }, + }, + rgap: 10, + }] + }, { + type: BI.VerticalLayout.xtype, + cls: processName, + width: 300, + height: 1, + }] + + }, { + el :{ + type: BI.VerticalLayout.xtype, + cls: "bi-error", + ref: (_ref: any) => { + this.errorInfo = _ref; + }, + invisible: true, + items : [{ + type: BI.Label.xtype, + height: 20, + textAlign: "left", + ref: (_ref: Label) => { + this.errorCode = _ref; + }, + },{ + type: BI.Label.xtype, + height: 20, + textAlign: "left", + ref: (_ref: Label) => { + this.errorMsg = _ref; + }, + }, { + type: BI.VerticalAdaptLayout.xtype, + rgap: 5, + items: [ + { + type: BI.Label.xtype, + text: BI.i18nText('Dec-Dcm_Connection_File_Upload_ErrorTip1'), + },{ + type: BI.TextButton.xtype, + cls: "bi-high-light bi-high-light-border-bottom", + text: BI.i18nText('Dec-Dcm_Connection_File_Upload_ErrorTip2'), + handler: function () { + window.open(Dec.system[DecCst.Hyperlink.DECISION_HYPERLINK_CONFIG][DecCst.Hyperlink.KERBEROS_CONF_HELP]); + }, + },{ + type: BI.Label.xtype, + text: BI.i18nText('Dec-Dcm_Connection_File_Upload_ErrorTip3'), + } + ], + },], + }, + vgap: 4, + lgap: 8, + } + ], + }, { + type: BI.MultifileEditor.xtype, + ref: (ref:any) => { + self.uploader = ref; + }, + url: this.model.uploadUrl, + accept: this.options.accept, + listeners: [ + { + // 选择文件 + eventName: BI.MultifileEditor.EVENT_CHANGE, + action: function (files) { + self.options.attachId = ''; + const fileInfo = files.files[0]; + self.setInfo(fileInfo); + self.store.setFileName(fileInfo.fileName); + this.upload(); + NProgress.configure({ parent: processClass, minimum: 0.0 }); + }, + }, + { + // 上传进度刷新 + eventName: BI.MultifileEditor.EVENT_PROGRESS, + action: function (progress) { + let rate = progress.loaded/progress.total; + NProgress.set(rate); + }, + }, + { + // 开始上传文件 + eventName: BI.MultifileEditor.EVENT_UPLOADSTART, + action: function (progressEvent, xhr) { + self.xhr = xhr; + }, + }, + { + // 上传文件完毕 + eventName: BI.MultifileEditor.EVENT_UPLOADED, + action: function () { + const uploadedInfo = this.getValue(); + const failed = BI.some(uploadedInfo, function (index, file) { + if (file.data.errorCode) { + BI.Msg.toast(uploadedInfo[0].filename + BI.i18nText('Dec-Dcm_Connection_File_Upload_Error'), { + level: "error", + }); + self.setErrorInfo(file.data) + return true; + } + }); + const key = self.options.processId +'Path'; + !failed && self.setValue(uploadedInfo[0].data.kerberosInfo[key]); + !failed && self.fireEvent(FileUpload.EVENT_CHECK_SUCCESS, uploadedInfo[0].data); + !failed && BI.Msg.toast(uploadedInfo[0].filename + BI.i18nText('Dec-Dcm_Connection_File_Upload_Success'),{ + level: "success" + }); + NProgress.configure({ parent: 'body'}); + + }, + }, + ], + }] + + }; + } + + public getValue(): string { + return this.options.value; + } + + public setValue(value: string) { + this.options.value = value; + this.textEditor.text.setTitle(value); + this.textEditor.setValue(value); + } + + public setInfo(info: any) { + this.uploadButton.setEnable(false); + this.fileInfo.setVisible(true); + this.textEditor.setValue(info.fileName); + this.fileName.setText(info.fileName); + this.fileSize.setText(Dec.Utils.getByteWidthUnit(info.size)); + this.fileModified.setText(BI.getDate().print("%Y-%X-%d %H:%M:%S")) + } + + public clearInfo() { + this.uploadButton.setEnable(true); + this.fileInfo.setVisible(false); + this.errorInfo.setVisible(false); + this.textEditor.setValue(''); + this.options.attachId = ''; + } + + public setErrorInfo(errorInfo: any) { + this.errorInfo.setVisible(true); + this.errorCode.setText(BI.i18nText("Dec-Dcm_Connection_File_Upload_ErrorCode") + ":"+ errorInfo.errorCode); + this.errorMsg.setText(BI.i18nText("Dec-Dcm_Connection_File_Upload_ErrorMsg") + ":" + errorInfo.errorMessage); + } + + public setEnable(v) { + this.uploadButton._setEnable(v); + } + + public setFileInfo(params) { + this.store.setFileInfo(params); + } +} diff --git a/src/modules/components/test_status/test_status.ts b/src/modules/components/test_status/test_status.ts index 0cfba82..a055e6e 100644 --- a/src/modules/components/test_status/test_status.ts +++ b/src/modules/components/test_status/test_status.ts @@ -29,6 +29,9 @@ export class TestStatus extends BI.Widget { failDriverMessage: Label; driverLink: FloatLeftLayout; detail: VerticalLayout; + failMaskers: any; + + extraContainer: VerticalLayout; watch = { status: (status: string) => { @@ -37,6 +40,7 @@ export class TestStatus extends BI.Widget { } render() { + const LAYOUT_WIDTH = 400; const { loadingCls, loadingText, successCls, successText, failCls, failText, retryText } = this.options; return { @@ -51,7 +55,7 @@ export class TestStatus extends BI.Widget { cls: 'bi-card', width: 450, height: 250, - single: true, + // single: true, showIndex: this.model.status, ref: (_ref: Tab) => { this.tab = _ref; @@ -70,6 +74,12 @@ export class TestStatus extends BI.Widget { tipCls: failCls, tipText: failText, retryText, + ref: (_ref: TipFail) => { + this.failMaskers = _ref; + if (BI.isEmptyString(this.failMessage.getText())) { + this.failMaskers.populateFail(BI.i18nText('Dec-Conn-ect-Failed'), false); + } + }, listeners: [ { eventName: TipFail.EVENT_RELOAD, @@ -116,10 +126,17 @@ export class TestStatus extends BI.Widget { scrolly: true, height: 75, items: [ + { + type: BI.VerticalLayout.xtype, + width: LAYOUT_WIDTH, + ref: (_ref: VerticalLayout) => { + this.extraContainer = _ref; + } + }, { type: BI.Label.xtype, whiteSpace: 'normal', - width: 400, + width: LAYOUT_WIDTH, textAlign: 'left', text: '', ref: (_ref: Label) => { @@ -168,7 +185,7 @@ export class TestStatus extends BI.Widget { this.store.setStatus(TEST_STATUS.SUCCESS); } - setFail(message: string, driver = '', link = '') { + setFail(message: string = '', driver = '', link = '') { this.store.setStatus(TEST_STATUS.FAIL); this.failMessage.setText(message); this.failDriverMessage.setVisible(!!driver); @@ -182,4 +199,14 @@ export class TestStatus extends BI.Widget { setLoading() { this.store.setStatus(TEST_STATUS.LOADING); } + + /** + * 设置报错弹窗自定义展示内容 + */ + setExtraContainer(container: Obj) { + BI.createWidget({ + ...container, + element: this.extraContainer, + }); + } } diff --git a/src/modules/components/test_status/tip_icon/tip_fail.ts b/src/modules/components/test_status/tip_icon/tip_fail.ts index 05ef93f..a12f157 100644 --- a/src/modules/components/test_status/tip_icon/tip_fail.ts +++ b/src/modules/components/test_status/tip_icon/tip_fail.ts @@ -1,5 +1,5 @@ import { shortcut, store } from '@core/core'; -import { Button } from '@fui/core'; +import { Button, Label } from '@fui/core'; import { TipFailModel } from './tip_fail.model'; @shortcut() @@ -21,7 +21,7 @@ export class TipFail extends BI.Widget { store: TipFailModel['store']; detailButton: Button; - + failText:Label; watch = { isCollapse: (isCollapse: boolean) => { this.detailButton.setText(isCollapse ? @@ -29,6 +29,10 @@ export class TipFail extends BI.Widget { BI.i18nText('Dec-Dcm_Connection_Detailed_Information')); }, } + populateFail(text:string,isVisible:boolean){ + this.failText.setText(text); + this.detailButton.setVisible(isVisible); + } render() { const { tipCls, tipText, retryText } = this.options; @@ -50,18 +54,20 @@ export class TipFail extends BI.Widget { }], }, { type: BI.Label.xtype, - height: 14, - bgap: 10, + _bgap: 10, text: tipText, + ref:(_ref:Label)=>{ + this.failText=_ref; + } }, { type: BI.VerticalAdaptLayout.xtype, - hgap: 5, + hgap: 12, items: [ { type: BI.Button.xtype, text: BI.i18nText('Dec-Dcm_Connection_Detailed_Information'), - level: 'ignore', + clear: true, ref: (_ref: Button) => { this.detailButton = _ref; }, @@ -72,8 +78,8 @@ export class TipFail extends BI.Widget { }, { type: BI.Button.xtype, + light: true, text: BI.i18nText('Dec-Dcm_Back'), - level: 'ignore', handler: () => { this.fireEvent(TipFail.EVENT_CLOSE); }, @@ -82,6 +88,7 @@ export class TipFail extends BI.Widget { type: BI.Button.xtype, text: retryText, handler: () => { + this.store.setIsCollapse(false); this.fireEvent(TipFail.EVENT_RELOAD); }, }, diff --git a/src/modules/components/text_checker/text_checker.ts b/src/modules/components/text_checker/text_checker.ts index c7984c0..339e1c2 100644 --- a/src/modules/components/text_checker/text_checker.ts +++ b/src/modules/components/text_checker/text_checker.ts @@ -3,87 +3,97 @@ import { Label, TextEditor } from '@fui/core'; @shortcut() export class TextChecker extends BI.Widget { - static xtype = 'dec.dcm.components.text_checker'; + public static xtype = 'dec.dcm.components.text_checker'; - props = { + public props = { width: 300, allowBlank: true, value: '', watermark: '', + inputType: 'text', + autocomplete: '', validationChecker: [] as { errorText: string; checker: (value: string) => boolean; autoFix?: boolean; }[], $value: '', - } + }; - textEditor: TextEditor; - errorLabel: Label; + public textEditor: TextEditor; + public errorLabel: Label; private isError: boolean; private value: string; private errorChecker: { errorText: string; checker: (value: string) => boolean; autoFix?: boolean; - } + }; - render() { - const { width, allowBlank, value, watermark, validationChecker, $value } = this.options; + public render() { + const { width, allowBlank, value, watermark, inputType, autocomplete, validationChecker, $value } = this.options; this.value = value; - + return { type: BI.AbsoluteLayout.xtype, width, height: 20, - items: [{ - el: { - type: BI.TextEditor.xtype, - $value, - width, - allowBlank, - value, - watermark, - ref: (_ref: TextEditor) => { - this.textEditor = _ref; - }, - listeners: [{ - eventName: BI.Editor.EVENT_CHANGE, - action: () => { - const value = this.getValue(); - if (value) { - this.errorChecker = validationChecker.find(item => item.checker && !item.checker(value)); - this.errorLabel.setText(BI.get(this.errorChecker, 'errorText')); - this.isError = !!BI.get(this.errorChecker, 'errorText'); - } else { - this.errorLabel.setText(''); - this.isError = false; - } - if (!this.isError) { - this.value = value; - } - this.fireEvent(BI.Editor.EVENT_CHANGE); - }, - }, { - eventName: BI.TextEditor.EVENT_BLUR, - action: () => { - if (BI.get(this.errorChecker, 'autoFix')) { - this.setValue(this.value); - this.errorLabel.setText(''); - } + items: [ + { + el: { + type: BI.TextEditor.xtype, + $value, + width, + allowBlank, + value, + watermark, + inputType, + autocomplete, + ref: (_ref: TextEditor) => { + this.textEditor = _ref; }, - }], + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + const value = this.getValue(); + if (value) { + this.errorChecker = validationChecker.find((item) => item.checker && !item.checker(value)); + this.errorLabel.setText(BI.get(this.errorChecker, 'errorText')); + this.isError = !!BI.get(this.errorChecker, 'errorText'); + } else { + this.errorLabel.setText(''); + this.isError = false; + } + if (!this.isError) { + this.value = value; + } + this.fireEvent(BI.Editor.EVENT_CHANGE); + }, + }, + { + eventName: BI.TextEditor.EVENT_BLUR, + action: () => { + if (BI.get(this.errorChecker, 'autoFix')) { + this.setValue(this.value); + this.errorLabel.setText(''); + } + }, + }, + ], + }, }, - }, { - el: { - type: BI.Label.xtype, - cls: 'bi-error', - ref: (_ref: Label) => { - this.errorLabel = _ref; + { + el: { + type: BI.Label.xtype, + cls: 'bi-error', + ref: (_ref: Label) => { + this.errorLabel = _ref; + }, }, + top: -15, }, - top: -15, - }], + ], }; } @@ -98,4 +108,8 @@ export class TextChecker extends BI.Widget { public setError(value: string) { this.errorLabel.setText(value); } + + public setWatermark(value: string) { + this.textEditor.setWaterMark(value); + } } diff --git a/src/modules/components/tips_combo/tips_combo.ts b/src/modules/components/tips_combo/tips_combo.ts new file mode 100644 index 0000000..c1b049a --- /dev/null +++ b/src/modules/components/tips_combo/tips_combo.ts @@ -0,0 +1,41 @@ +import { shortcut } from '@core/core'; +import { BubbleCombo, BubblePopupView, IconButton } from '@fui/core'; + +@shortcut() +export class TipsCombo extends BI.Widget { + public static xtype = 'dec.dcm.tips.combo'; + + public props: BubblePopupView['props'] & IconButton['props'] = { + trigger: 'hover', + direction: 'top' + }; + + private bubbleCombo: BubbleCombo; + + private bubbleComboPopup: BubblePopupView; + + public render() { + const { direction, trigger, el } = this.options; + + return { + type: BI.BubbleCombo.xtype, + trigger, + direction, + el: { + type: BI.IconButton.xtype, + cls: "detail-font", + }, + popup: { + type: BI.BubblePopupView.xtype, + ref: (_ref: BubblePopupView) => { + this.bubbleComboPopup = _ref; + }, + el, + }, + listeners: [], + ref: (_ref: BubbleCombo) => { + this.bubbleCombo = _ref; + } + } + } +} \ No newline at end of file diff --git a/src/modules/constants/constant.ts b/src/modules/constants/constant.ts index 5bb3778..e36e16a 100644 --- a/src/modules/constants/constant.ts +++ b/src/modules/constants/constant.ts @@ -5,6 +5,11 @@ export const PAGE_INDEX = { POOL: 'pool', }; +export const DATABASE_TYPE = { + SAP_HANA: "sap-hana", + STAR_ROCKS :'starrocks', +}; + export const OTHER_JDBC = 'otherJDBC'; export const DEFAULT_HELP_LINK = 'databaseHelpLink'; @@ -279,7 +284,7 @@ export const DATA_BASE_TYPES = [ text: 'ClickHouse', databaseType: 'clickhouse', driver: 'ru.yandex.clickhouse.ClickHouseDriver', - url: 'jdbc:clickhouse://hostname:port', + url: 'jdbc:clickhouse://hostname:port/database', commonly: false, internal: true, type: 'jdbc', @@ -459,6 +464,7 @@ export const DATA_BASE_TYPES = [ text: 'KINGBASE', databaseType: 'kingbase', driver: 'com.kingbase.Driver', + versions: ['KingbaseES 7.0'], url: 'jdbc:kingbase://hostname:port/database', commonly: false, internal: true, @@ -537,7 +543,7 @@ export const DATA_BASE_TYPES = [ kerberos: false, urls: { 'org.postgresql.Driver': 'jdbc:postgresql://hostname:port/dbname', - 'com.pivotal.jdbc.GreenplumDriver': 'jdbc:pivotal:greenplum://hostname:port;dbname', + 'com.pivotal.jdbc.GreenplumDriver': 'jdbc:pivotal:greenplum://hostname:port;DatabaseName=dbname', }, }, { text: 'Postgresql', @@ -561,6 +567,39 @@ export const DATA_BASE_TYPES = [ type: 'jdbc', hasSchema: false, kerberos: false, + }, { + text: 'Doris', + databaseType: 'doris', + driver: 'com.mysql.jdbc.Driver', + drivers: ['com.mysql.jdbc.Driver'], + url: 'jdbc:mysql://hostname:port/database', + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: false, + kerberos: false, + }, { + text: BI.i18nText('Dec-Dcm_Connection_Database_DM'), + databaseType: 'dm', + driver: 'dm.jdbc.driver.DmDriver', + drivers: ['dm.jdbc.driver.DmDriver'], + url: 'jdbc:dm://hostname:port', + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: true, + kerberos: true, + }, { + text: 'dremio', + databaseType: 'dremio', + driver: 'com.dremio.jdbc.Driver', + drivers: ['com.dremio.jdbc.Driver'], + url: 'jdbc:dremio:direct=hostname:31010', + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: true, + kerberos: false, }, { text: 'Presto', databaseType: 'presto', @@ -575,7 +614,7 @@ export const DATA_BASE_TYPES = [ text: 'SAP HANA', databaseType: 'sap-hana', driver: 'com.sap.db.jdbc.Driver', - url: 'jdbc:sap://hostname:port?reconnect=true', + url: 'jdbc:sap://hostname:port?databaseName=database&reconnect=true', commonly: false, internal: true, type: 'jdbc', @@ -659,6 +698,42 @@ export const DATA_BASE_TYPES = [ 'org.sqlite.JDBC': 'jdbc:sqlite:[PATH_TO_DB_FILES]', }, }, + { + text: 'trino', + databaseType: 'trino', + driver: 'io.trino.jdbc.TrinoDriver', + url: 'jdbc:trino://hostname:port/database', + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: true, + kerberos: false, + }, + { + text: 'TDSQL', + databaseType: 'tdsql', + driver: 'org.postgresql.Driver', + url: 'jdbc:postgresql://hostname:port/database', + versions: ['pgsql'], + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: true, + kerberos: false, + fetchSize: 10000, + }, + { + text: BI.i18nText('StarRocks'), + databaseType: 'starrocks', + driver: 'com.mysql.jdbc.Driver', + drivers: ['com.mysql.jdbc.Driver'], + url: 'jdbc:mysql://hostname:port/default_catalog.database', + commonly: false, + internal: true, + type: 'jdbc', + hasSchema: false, + kerberos: false, + } ]; @@ -701,6 +776,28 @@ export const CONNECT_CHARSET = [ }, ]; +export const CONNECT_SSH_TYPE = [ + { + text: BI.i18nText('Dec-Dcm_Connection_Form_Password'), + value: 'NORMAL', + privateKeyPathFormVisible: false, + secretFormName: BI.i18nText('Dec-Dcm_Connection_Form_Password'), + }, + { + text: BI.i18nText('Dec-Dcm_Connection_Form_PublicKey'), + value: 'KEY', + privateKeyPathFormVisible: true, + secretFormName: BI.i18nText('Dec-Dcm_Connection_Form_Passphrase'), + }, +]; + +export const CONNECT_SSL_TYPE = [ + { + text: BI.i18nText('Dec-Dcm_Connection_Form_Password'), + value: 'NORMAL', + }, +]; + export const TEST_STATUS = { LOADING: 'loading', SUCCESS: 'success', @@ -737,15 +834,18 @@ export const DEFAULT_JDBC_POOL = { minIdle: 0, maxWait: 10000, testOnBorrow: true, + keepAlive: true, testOnReturn: false, testWhileIdle: false, - timeBetweenEvictionRunsMillis: -1, + timeBetweenEvictionRunsMillis: 60000, numTestsPerEvictionRun: 3, minEvictableIdleTimeMillis: 1800, + maxEvictableIdleTimeMillis: 25200, + keepAliveBetweenTimeMillis: 120000, }; export const CONNECTION_LAYOUT = { - hgap: 5, + hgap: 10, vgap: 15, labelHeight: 24, }; @@ -776,3 +876,14 @@ export const JNDI_FACTORYS = [ export const PAGE_SIZE = 50; export const INT_MAX_VALUE = 2147483647; export const INT_MIN_VALUE = -2147483648; + +export const YES_OR_NO = [ + { + text: BI.i18nText('Dec-Basic_Yes'), + value: 1, + }, + { + text: BI.i18nText('Dec-Basic_No'), + value: 0, + } +] diff --git a/src/modules/core/checkIllegalStrings/checkIllegalStrings.ts b/src/modules/core/checkIllegalStrings/checkIllegalStrings.ts new file mode 100644 index 0000000..ddc4f99 --- /dev/null +++ b/src/modules/core/checkIllegalStrings/checkIllegalStrings.ts @@ -0,0 +1,52 @@ +/* + https://work.fineres.com/browse/REPORT-91724 用于参数统一校验 + */ +import { ILLEGAL_STRINGS } from "./constant"; +export type CheckResult = { + legal: boolean, + errorMsg: string, +} +export const CHECK_CORRECT: CheckResult = { + legal: true, + errorMsg: "", +}; + +/** + * 检测非法字符,返回错误提示 + * @param value 要校验的字符串 + */ +export function checkIllegalStrings(value: string): CheckResult { + // 后端传入的校验开关,如果没传,那也默认开启 + const enabled = Dec.system.enableParameterVerify ?? true; + let result = CHECK_CORRECT; + if (enabled) { + // 关键字不区分大小写 + ILLEGAL_STRINGS.every(s => { + const sIndex = value.toLowerCase().indexOf(s); + if (sIndex !== -1) { + result = { + legal: false, + errorMsg: `${BI.i18nText("Dec-Basic_Check_Illegal_Strings")}${value.substr(sIndex, s.length)}`, + }; + + return false; + } + + return true; + }); + + return result; + } + + return result; +} + +export function checkIllegalStringsInWidgetAndShowError(widget: any) { + const value = widget.getValue(); + const result = checkIllegalStrings(value); + if (!result.legal) { + widget.showError(result.errorMsg); + } + + return result.legal; +} diff --git a/src/modules/core/checkIllegalStrings/constant.ts b/src/modules/core/checkIllegalStrings/constant.ts new file mode 100644 index 0000000..351c185 --- /dev/null +++ b/src/modules/core/checkIllegalStrings/constant.ts @@ -0,0 +1,15 @@ +/** + * 参数检验的非法字符数组,由于不区分大小写,统一用小写 + */ +export const ILLEGAL_STRINGS = [ + "\"", + "<", + ">", + "&", + "/script", + "javascript:", + "onblur", + "getruntime", + "processbuilder", + "java.lang.processimpl", +]; diff --git a/src/modules/core/index.ts b/src/modules/core/index.ts new file mode 100644 index 0000000..efe056c --- /dev/null +++ b/src/modules/core/index.ts @@ -0,0 +1 @@ +export { checkIllegalStringsInWidgetAndShowError, checkIllegalStrings } from "./checkIllegalStrings/checkIllegalStrings" diff --git a/src/modules/crud/api.ts b/src/modules/crud/api.ts index fa0f2d4..9c5b7ba 100644 --- a/src/modules/crud/api.ts +++ b/src/modules/crud/api.ts @@ -5,6 +5,7 @@ import { ConnectionPoolType, SocketResult, ResultType, + checkDriverStatusParams, } from './crud.typings'; export interface Api { @@ -46,6 +47,17 @@ export interface Api { */ testConnection(data: Connection): Promise; + /** + * 获取驱动加载路径 + */ + getDriverLoadPath(data: Connection): Promise>; + + /** + * 检测驱动冲突状态 + * @param data 驱动路径 + */ + checkDriverStatus(data: checkDriverStatusParams): Promise>; + /** * 获取连接池数据 * @param name @@ -99,4 +111,9 @@ export interface Api { * 获取外链 */ getHyperlink(name: string): string; + + /** + * 获取JNDI数据库类型可用状态 + */ + getJNDIDatabaseStatus(): Promise<{ data?: boolean }>; } diff --git a/src/modules/crud/crud.service.ts b/src/modules/crud/crud.service.ts index 19297e7..71c9604 100644 --- a/src/modules/crud/crud.service.ts +++ b/src/modules/crud/crud.service.ts @@ -15,7 +15,7 @@ export function requestGet(url: string, data?: any): Promise { export function requestPost(url: string, data = {}): Promise { return new Promise(resolve => { - Dec.reqPost(getFullUrl(url), data, re => { + Dec.reqByEncrypt("POST", getFullUrl(url), data, re => { resolve(re); }); }); @@ -23,7 +23,7 @@ export function requestPost(url: string, data = {}): Promise { export function requestDelete(url: string, data = {}) { return new Promise(resolve => { - Dec.reqDelete(getFullUrl(url), data, re => { + Dec.reqByEncrypt("DELETE", getFullUrl(url), data, re => { resolve(re); }); }); @@ -31,7 +31,7 @@ export function requestDelete(url: string, data = {}) { export function requestPut(url: string, data = {}) { return new Promise(resolve => { - Dec.reqPut(getFullUrl(url), data, re => { + Dec.reqByEncrypt("PUT", getFullUrl(url), data, re => { resolve(re); }); }); diff --git a/src/modules/crud/crud.typings.d.ts b/src/modules/crud/crud.typings.d.ts index 9f0f69f..8bc01bd 100644 --- a/src/modules/crud/crud.typings.d.ts +++ b/src/modules/crud/crud.typings.d.ts @@ -1,3 +1,7 @@ +export interface CrudParams { + [key: string]: string | number | { [key: string]: any }; +} + export interface CrudReqOpts { url?: string; type?: 'GET' | 'POST' | 'DELETE' | 'PUT'; @@ -9,28 +13,116 @@ export interface CrudReqOpts { params?: CrudParams; } -export interface CrudParams { - [key: string]: string | number | { [key: string]: any }; +export interface ConnectionLicInfo { + currentConnectionNum: number; + maxConnectionNum: number; } -export interface Connection { - connectionId: string; - connectionType: string; - connectionName: string; - creator?: string; - connectionData: ConnectionJDBC | ConnectionJNDI | ConnectionPlugin | string; - privilegeDetailBeanList?: { - privilegeType: number; - privilegeValue: number; - }[]; +export interface ConnectionPoolType { + maxActive: number; + maxIdle: number; + numActive: number; + numIdle: number; } -export interface ConnectionLicInfo { - currentConnectionNum: number; - maxConnectionNum: number; +type ConnectionDataOfSSH = { + usingSsh: boolean; // 使用SSH通道 + sshIp: string; // 主机 + sshPort: number; // 端口 + sshUser: string; // 用户名 + redirectPort: number; + redirectIp: string; + sshTimeOut: number; + sshKeepAlive: number; +} & ( + | { + sshType: 'NORMAL'; // 验证方法:密码 + sshPrivateKeyPath: ''; // 没啥意义,该验证方法下为空字符串 + sshSecret: string; // 密码 + } + | { + sshType: 'KEY'; // 验证方法:公钥 + sshPrivateKeyPath: string; // 私钥 + sshSecret: string; // 密码短语 + } + ); + +type ConnectionDataOfSSL = { + usingSsl: boolean; // 使用SSL通道 + sslType: 'NORMAL'; // SSL类型,只有NORMAL一种 + caCertificate: string; // CA证书 + verifyCa: boolean; // 验证针对CA的服务器证书 + sslClientPrivateKey: string; // 客户端密钥 + sslClientCertificate: string; // 客户端证书 +}; + +export interface ConnectionPoolJDBC { + /** + * 初始化连接数量 + */ + initialSize?: number; + /** + * 最大连接数 + */ + maxActive?: number; + /** + * 最大空闲数 + */ + maxIdle?: number; + /** + * 最小空闲数 + */ + minIdle?: number; + /** + * 最大等待时间 + */ + maxWait?: number; + /** + * sql查询 + */ + validationQuery?: string; + + /** + * 连接前校验 + */ + testOnBorrow?: boolean; + + /** + * 空闲连接可用性定期检查 + */ + keepAlive?: boolean; + /** + * 归还前校验 + */ + testOnReturn?: boolean; + + /** + * 空闲校验 + */ + testWhileIdle?: boolean; + /** + * 在空闲连接回收器线程运行期间休眠的时间值,毫秒。 + */ + timeBetweenEvictionRunsMillis?: number; + /** + * 每次空闲连接回收器现成运行时检查的连接数量 + */ + numTestsPerEvictionRun?: number; + /** + * 连接在池中保持空闲而不被空闲连接回收器回收的最小时间,单位毫秒 + */ + minEvictableIdleTimeMillis?: number; + /** + * 连接在池中保持空闲而不被空闲连接回收器回收的最小时间,单位毫秒 + */ + maxEvictableIdleTimeMillis?: number; + /** + * 空闲连接可用性定期检查时间阈值 + */ + keepAliveBetweenTimeMillis?: number; } -export interface ConnectionJDBC { +export type ConnectionJDBC = { /** * 数据库名称 */ @@ -44,6 +136,10 @@ export interface ConnectionJDBC { * 驱动 */ driver: string; + /** + * 驱动来源 + */ + driverSource: 'default' | 'custom'; /** * 数据库连接url */ @@ -108,6 +204,10 @@ export interface ConnectionJDBC { * 秘钥路径 */ keyPath?: string; + /** + * krb5.conf文件 + */ + krb5Path?: string; /** * fetchSize */ @@ -118,71 +218,45 @@ export interface ConnectionJDBC { identity?: string; connectionPoolAttr: ConnectionPoolJDBC; -} - -export interface ConnectionPoolJDBC { - /** - * 初始化连接数量 - */ - initialSize?: number; - /** - * 最大连接数 - */ - maxActive?: number; - /** - * 最大空闲数 - */ - maxIdle?: number; - /** - * 最小空闲数 - */ - minIdle?: number; /** - * 最大等待时间 + * 并行装载 */ - maxWait?: number; + parallelLoad?: IParallelLoad; /** - * sql查询 + * HDFS */ - validationQuery?: string; - - /** - * 连接前校验 - */ - testOnBorrow?: boolean; + hdfs?: { + /** + * HDFS地址 + */ + hdfsAddress?: string; + }; +} & ConnectionDataOfSSH & ConnectionDataOfSSL; +/** + * 并行装载 + */ +export interface IParallelLoad { /** - * 归还前校验 + * 服务器地址 */ - testOnReturn?: boolean; - + serverAddress?: string; /** - * 空闲校验 + * 服务器地址选项 */ - testWhileIdle?: boolean; + serverAddressItems?: string[]; /** - * 在空闲连接回收器线程运行期间休眠的时间值,毫秒。 + * 复用临时表 */ - timeBetweenEvictionRunsMillis?: number; + reuseTemporaryTable?: string; /** - * 每次空闲连接回收器现成运行时检查的连接数量 + * 临时文件条数限制 */ - numTestsPerEvictionRun?: number; + filePiecesLimit?: string; /** - * 连接在池中保持空闲而不被空闲连接回收器回收的最小时间,单位毫秒 + * 临时文件大小限制 */ - minEvictableIdleTimeMillis?: number; -} - -export interface ConnectionJNDI { - jndiName: string; - /** - * 编码 - */ - originalCharsetName: string; - newCharsetName: string; - creator?: string; - contextHashtable: ContextHashtable; + fileSizeLimit?: string } export interface ContextHashtable { @@ -203,33 +277,54 @@ export interface ContextHashtable { 'java.naming.applet': string; } +export interface ConnectionJNDI { + jndiName: string; + /** + * 编码 + */ + originalCharsetName: string; + newCharsetName: string; + creator?: string; + contextHashtable: ContextHashtable; +} + export interface ConnectionPlugin { pluginType: 'json'; creator: ''; pluginData: any; } +export interface Connection { + connectionId: string; + connectionType: string; + connectionName: string; + creator?: string; + connectionData: ConnectionJDBC | ConnectionJNDI | ConnectionPlugin | string; + privilegeDetailBeanList?: { + privilegeType: number; + privilegeValue: number; + }[]; +} + export interface TestRequest { data?: string[]; errorCode?: string; errorMsg?: string; } -export interface ConnectionPoolType { - maxActive: number; - maxIdle: number; - numActive: number; - numIdle: number; -} - export interface SocketResult { data?: string; errorCode?: string; errorMsg?: string; } -export interface ResultType { - data?: any; +export interface ResultType { + data?: T; errorCode?: string; errorMsg?: string; } + +export type checkDriverStatusParams = { + path: string; + driver: ConnectionJDBC['driver']; +} \ No newline at end of file diff --git a/src/modules/crud/decision.api.ts b/src/modules/crud/decision.api.ts index 7b8bc37..6207d6f 100644 --- a/src/modules/crud/decision.api.ts +++ b/src/modules/crud/decision.api.ts @@ -1,5 +1,5 @@ import { Api } from './api'; -import { Connection, TestRequest, ConnectionPoolType, SocketResult, ConnectionLicInfo } from './crud.typings'; +import { Connection, TestRequest, ConnectionPoolType, SocketResult, ConnectionLicInfo, ResultType, checkDriverStatusParams } from './crud.typings'; import { requestGet, requestDelete, requestPost, requestPut } from './crud.service'; import { editStatusEvent, errorCode } from '@constants/env'; @@ -48,6 +48,27 @@ export class DecisionApi implements Api { return requestPost('test', form); } + /** + * 获取驱动加载路径 + * @returns + */ + getDriverLoadPath(data: Connection): Promise> { + const form = { + ...data, + connectionData: JSON.stringify(data.connectionData), + }; + + return requestPost('driver/path', form); + } + + /** + * 检测驱动冲突状态 + * @param data 驱动路径 + */ + checkDriverStatus(data: checkDriverStatusParams): Promise> { + return requestGet(Dec.Utils.getEncodeURL('test/driver/conflict', '', data)); + } + getConnectionPool(name: string): Promise<{ data?: ConnectionPoolType }> { return requestGet(`pool/info?connectionName=${encodeURIComponent(name)}`, {}); } @@ -118,18 +139,39 @@ export class DecisionApi implements Api { getCipher(password: string) { return BI.Providers.getProvider('dec.provider.cipher') - .getCipher(password); + .getCompleteCipher(password); } getPlain(cipher: string) { return BI.Providers.getProvider('dec.provider.cipher') - .getPlain(cipher); + .getCompletePlain(cipher); } getHyperlink(name: string) { return Dec.system[DecCst.Hyperlink.DECISION_HYPERLINK_CONFIG][name]; } + changePrincipal(value: any) { + return requestPost(`switch/principal`, value); + } + + getPrincipals(keytab: string) { + return requestGet(`/principals?keytabPath=${keytab}`, {}); + } + + getTimeOut(): Promise<{ data?: any }> { + return requestGet('kdc/timeout', {}); + } + + putTimeOut(value: number) { + return requestPut(`kdc/timeout?timeout=${value}`, {}) + } + + // 获取当前lic是否可以使用JNDI数据库类型 + getJNDIDatabaseStatus(): Promise<{ data?: boolean }> { + return requestGet('databasetype/limit', {}); + } + private sendEditStatusEvent(name: string, type: string): Promise { return new Promise(resolve => { if (Dec?.socket?.connected) { diff --git a/src/modules/crud/design.api.ts b/src/modules/crud/design.api.ts index f010918..d629b16 100644 --- a/src/modules/crud/design.api.ts +++ b/src/modules/crud/design.api.ts @@ -1,5 +1,5 @@ import { Api } from './api'; -import { Connection, TestRequest, ConnectionPoolType, SocketResult, ConnectionLicInfo } from './crud.typings'; +import { Connection, TestRequest, ConnectionPoolType, SocketResult, ConnectionLicInfo, ResultType, ConnectionJDBC, checkDriverStatusParams } from './crud.typings'; import { requestGet } from './crud.service'; // TODO: 此页面的接口等待设计器提供相应的方法 @@ -39,6 +39,27 @@ export class DesignApi implements Api { }); } + /** + * 获取驱动加载路径 + * @param name + * @returns + */ + getDriverLoadPath(data: Connection): Promise> { + return new Promise(resolve => { + resolve({ data: '' }); + }); + } + + /** + * 检测驱动冲突状态 + * @param data 驱动路径 + */ + checkDriverStatus(data: checkDriverStatusParams): Promise> { + return new Promise(resolve => { + resolve({ data: false }); + }); + } + getConnectionPool(name: string): Promise<{ data: ConnectionPoolType }> { return new Promise(resolve => { resolve({ @@ -88,4 +109,8 @@ export class DesignApi implements Api { // 设计器获取超链 return ''; } + + getJNDIDatabaseStatus() { + return Promise.resolve({ data: true }); + } } diff --git a/src/modules/pages/__point__/connect.point.ts b/src/modules/pages/__point__/connect.point.ts new file mode 100644 index 0000000..576f153 --- /dev/null +++ b/src/modules/pages/__point__/connect.point.ts @@ -0,0 +1,24 @@ +BI.point("dec.dcm.model.connection", "createNewConnection", () => { + Dec.Utils.saveFocusPoint({ + id: "E73325", + title: "新建数据连接", + }); +}); + +BI.point("dec.dcm.model.title_maintain", "setTestEvent", () => { + Dec.Utils.saveFocusPoint({ + id: "E73328", + title: "测试数据连接", + }); +}); + +BI.point("dec.dcm.model.maintain_form", "addConnection", function () { + Dec.Utils.saveFocusPoint({ + id: "E8827", + title: "保存数据连接", + body: { + datebaseType: this.model.datebaseTypeSelected, + databaseName: this.model.connectionSelected, + }, + }); +}); \ No newline at end of file diff --git a/src/modules/pages/connection/connection.model.ts b/src/modules/pages/connection/connection.model.ts index f037865..97daf69 100644 --- a/src/modules/pages/connection/connection.model.ts +++ b/src/modules/pages/connection/connection.model.ts @@ -13,13 +13,14 @@ export class ConnectionModel extends Model<{ connectionSelected: AppModel['TYPE']['connectionSelected']; connectionSelectedOne: AppModel['TYPE']['connectionSelectedOne']; datebaseTypeSelected: AppModel['TYPE']['datebaseTypeSelected']; + noTestConnection: AppModel['TYPE']['noTestConnection']; }, childContext: ConnectionModel['childContext']; context: ConnectionModel['context']; }> { static xtype = 'dec.dcm.model.connection'; - context = ['pageIndex', 'connectionSelected', 'connectionSelectedOne', 'datebaseTypeSelected', 'connectionLicInfo']; + context = ['pageIndex', 'connectionSelected', 'connectionSelectedOne', 'datebaseTypeSelected', 'connectionLicInfo', 'noTestConnection']; actions = { initConnectionLicInfo: (cb: Function) => { @@ -53,8 +54,14 @@ export class ConnectionModel extends Model<{ this.model.connectionSelected = name; }, getConnectionStatus() { + if (this.model.connectionSelectedOne.pluginConnection) { + return Promise.resolve(); + } return api.getConnectionStatus(this.model.connectionSelected); }, + setNoTestConnection(value: boolean) { + this.model.noTestConnection = value; + }, checkConnectionLic() { return this.model.connectionLicInfo.currentConnectionNum > this.model.connectionLicInfo.maxConnectionNum; }, diff --git a/src/modules/pages/connection/connection.ts b/src/modules/pages/connection/connection.ts index f8d175c..1215974 100644 --- a/src/modules/pages/connection/connection.ts +++ b/src/modules/pages/connection/connection.ts @@ -24,24 +24,17 @@ export class Connection extends BI.Widget { title: HTapeLayout; watch = { - connectionSelected: (name: string) => { - if (name) { - const canEdit = connectionCanEdit(this.model.connectionSelectedOne); - const type = this.getSelectConnectionType(); - this.connectionTitleWidget.setText(`${name}(${getTextByDatabaseType(type)})`); - this.connectionEditWidget.setVisible(canEdit); - const hasRegistered = this.hasRegistered(); - this.title.setVisible(hasRegistered); - if (!hasRegistered) { - this.listView.populate(BI.createItems(this.renderNoRegistered())); - } else { - this.listView.populate(BI.createItems(this.renderItems())); - } - } else { - this.listView.populate(BI.createItems(this.renderEmpty())); - this.connectionTitleWidget.setText(''); - this.connectionEditWidget.setVisible(false); - } + connectionSelectedOne: { + immediate: true, + handler: (v: Connection) => { + BI.nextTick(() => { + const connectionName = v.connectionName; + + connectionName + ? this.renderConnectionListView(connectionName) + : this.renderEmptyListView(); + }); + }, }, }; @@ -50,8 +43,6 @@ export class Connection extends BI.Widget { } render() { - this.store.setConnectionSelected(''); - return { type: BI.HTapeLayout.xtype, hgap: 10, @@ -71,6 +62,9 @@ export class Connection extends BI.Widget { { type: BI.Button.xtype, text: BI.i18nText('Dec-Dcm_Connection_New'), + title: BI.i18nText('Dec-Dcm_Connection_New'), + minWidth: 0, + width: 98, handler: () => { this.store.createNewConnection(); }, @@ -79,7 +73,7 @@ export class Connection extends BI.Widget { right: [ { type: 'dec.connection.driver.entry', - invisible: true, + invisible: !BI.Services.getService('dec.service.global').isAdmin(), from: '.dec-dcm', listeners: [ { @@ -134,16 +128,17 @@ export class Connection extends BI.Widget { }, handler: () => { this.store.getConnectionStatus() - .then(re => { - this.store.setPageIndex(PAGE_INDEX.MAINTAIN); - this.store.setDatebaseTypeSelected(''); + .then(() => { + const databaseType = this.model.connectionSelectedOne.connectionType; + const database = BI.find(getAllDatabaseTypes(), (_index, value) => value.databaseType === databaseType); + this.setMaintainPage(); + this.store.setNoTestConnection(database.isHideConnection); }) .catch(() => { }); }, }], }, - width: 90, }, ], }, @@ -210,12 +205,6 @@ export class Connection extends BI.Widget { }]; } - private renderEmpty() { - return [{ - type: BI.Layout.xtype, - }]; - } - private hasRegistered() { const allDatabaseTypes = getAllDatabaseTypes(); switch (this.model.connectionSelectedOne.connectionType) { @@ -237,4 +226,37 @@ export class Connection extends BI.Widget { return databaseType; } + + private setMaintainPage() { + this.store.setPageIndex(PAGE_INDEX.MAINTAIN); + this.store.setDatebaseTypeSelected(''); + } + + private renderConnectionListView(name: string) { + const canEdit = connectionCanEdit(this.model.connectionSelectedOne), + type = this.getSelectConnectionType(), + hasRegistered = this.hasRegistered(); + + this.connectionTitleWidget.setText(`${name}(${getTextByDatabaseType(type)})`); + this.connectionEditWidget.setVisible(canEdit); + + this.title.setVisible(hasRegistered); + + hasRegistered + ? this.listView.populate(BI.createItems(this.renderItems())) + : this.listView.populate(BI.createItems(this.renderNoRegistered())); + } + + private renderEmptyListView() { + this.listView.populate( + BI.createItems([ + { + type: BI.Layout.xtype, + } + ]) + ); + + this.connectionTitleWidget.setText(''); + this.connectionEditWidget.setVisible(false); + } } diff --git a/src/modules/pages/connection/connection_jdbc/connection_jdbc.ts b/src/modules/pages/connection/connection_jdbc/connection_jdbc.ts index 6f97be8..893e960 100644 --- a/src/modules/pages/connection/connection_jdbc/connection_jdbc.ts +++ b/src/modules/pages/connection/connection_jdbc/connection_jdbc.ts @@ -1,195 +1,405 @@ -import { shortcut, store } from '@core/core'; -import { FormItem } from '../components/form_item/form_item'; -import { Collapse, EVENT_CHANGE } from 'src/modules/components/collapse/collapse'; -import { ConnectionJdecModel } from './connection_jdbc.model'; -import { ConnectionJDBC } from 'src/modules/crud/crud.typings'; -import { getAllDatabaseTypes, getJdbcDatabaseType, resolveUrlInfo } from '../../../app.service'; -import { CONNECTION_LAYOUT } from '@constants/constant'; -import { VerticalLayout } from '@fui/core'; -import { ApiFactory } from '../../../crud/apiFactory'; - -const api = new ApiFactory().create(); - -@shortcut() -@store(ConnectionJdecModel) -export class ConnectionJdbc extends BI.Widget { - static xtype = 'dec.dcm.connection_jdbc'; - - advancedSet: any; - model: ConnectionJdecModel['model']; - allDatabaseTypes = getAllDatabaseTypes(); - - render() { - const connectionData = this.model.connectionSelectedOne.connectionData as ConnectionJDBC; - const { - driver, - driverSource, - database, - user, - originalCharsetName, - schema, - connectionPoolAttr, - authType, - principal, - url, - fetchSize, - } = connectionData; - const databaseType = getJdbcDatabaseType(database, driver); - const { host, port, databaseName } = resolveUrlInfo(url, database); - const { hgap, vgap } = CONNECTION_LAYOUT; - - return { - type: BI.VerticalLayout.xtype, - hgap, - vgap, - items: [ - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Driver'), - value: BI.isKey(driverSource) ? `${driver} (${driverSource})` : driver, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), - value: databaseName, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), - value: host, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), - value: port, - }, - authType ? - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_AuthType'), - value: authType, - } : { - type: BI.Layout.xtype, - }, - { - type: FormItem.xtype, - name: authType ? BI.i18nText('Dec-Dcm_Connection_Form_Principal') : BI.i18nText('Dec-Dcm_Connection_Form_UserName'), - value: authType ? principal : user, - }, - { - type: FormItem.xtype, - name: authType ? BI.i18nText('Dec-Dcm_Connection_Form_KeyPath') : BI.i18nText('Dec-Dcm_Connection_Form_Password'), - value: '******', - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_OriginalCharsetName'), - value: originalCharsetName ? originalCharsetName : BI.i18nText('Dec-Dcm_Connection_Form_Default'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Pattern'), - value: schema, - invisible: !databaseType.hasSchema, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), - value: url, - }, - { - type: Collapse.xtype, - width: 70, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Advanced_Setting'), - listeners: [ - { - eventName: EVENT_CHANGE, - action: (isCollapse: boolean) => { - this.advancedSet.setVisible(!isCollapse); - }, - }, - ], - }, - { - type: BI.VerticalLayout.xtype, - tgap: -15, - vgap, - invisible: true, - ref: (_ref: VerticalLayout) => { - this.advancedSet = _ref; - }, - items: [ - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), - value: connectionPoolAttr.initialSize, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), - value: connectionPoolAttr.maxActive, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Idle'), - value: connectionPoolAttr.maxIdle, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), - value: connectionPoolAttr.minIdle, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), - value: connectionPoolAttr.maxWait, - unit: BI.i18nText('Dec-Dcm_Millisecond'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Validation_Query'), - value: api.getPlain(connectionPoolAttr.validationQuery || ''), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), - value: connectionPoolAttr.testOnBorrow ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), - value: connectionPoolAttr.testOnReturn ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), - value: connectionPoolAttr.testWhileIdle ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), - value: connectionPoolAttr.timeBetweenEvictionRunsMillis, - unit: BI.i18nText('Dec-Dcm_Millisecond'), - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), - value: connectionPoolAttr.numTestsPerEvictionRun, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), - value: connectionPoolAttr.minEvictableIdleTimeMillis, - unit: BI.i18nText('BI-Basic_Seconds'), - }, { - type: FormItem.xtype, - invisible: fetchSize < 0 && fetchSize !== -2, - name: 'Fetchsize', - value: fetchSize === -2 ? '' : fetchSize, - }, - ], - }, - ], - }; - } -} +import { shortcut, store } from '@core/core'; +import { FormItem } from '../components/form_item/form_item'; +import { Collapse, EVENT_CHANGE } from 'src/modules/components/collapse/collapse'; +import { ConnectionJdecModel } from './connection_jdbc.model'; +import { ConnectionJDBC } from 'src/modules/crud/crud.typings'; +import { getAllDatabaseTypes, getJdbcDatabaseType, resolveUrlInfo } from '../../../app.service'; +import { CONNECTION_LAYOUT, CONNECT_SSH_TYPE } from '@constants/constant'; +import { VerticalLayout } from '@fui/core'; +import { ApiFactory } from '../../../crud/apiFactory'; + +const api = new ApiFactory().create(); + +@shortcut() +@store(ConnectionJdecModel) +export class ConnectionJdbc extends BI.Widget { + static xtype = 'dec.dcm.connection_jdbc'; + + model: ConnectionJdecModel['model']; + allDatabaseTypes = getAllDatabaseTypes(); + + sshSet: VerticalLayout; + sslSet: VerticalLayout; + advancedSet: VerticalLayout; + parallelLoadSet: VerticalLayout; + + render() { + const connectionData = this.model.connectionSelectedOne.connectionData as ConnectionJDBC; + const { + driver, + driverSource, + database, + user, + originalCharsetName, + schema, + connectionPoolAttr, + authType, + principal, + url, + fetchSize, + // ssh + usingSsh, + sshIp, + sshPort, + sshUser, + sshType, + sshSecret, + sshPrivateKeyPath, + // ssl + usingSsl, + caCertificate, + verifyCa, + sslClientPrivateKey, + sslClientCertificate, + // 并行装载 + parallelLoad, + // HDFS + hdfs, + } = connectionData; + const databaseType = getJdbcDatabaseType(database, driver); + const { host, port, catalog, databaseName, version } = resolveUrlInfo(url, database); + this.version = !BI.isUndefined(databaseType.versions) ? (version ?? databaseType.versions[0]) : version; + const { hgap, vgap } = CONNECTION_LAYOUT; + + return { + type: BI.VerticalLayout.xtype, + hgap, + vgap, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Basic_Version'), + invisible: BI.isUndefined(this.version), + value: BI.i18nText('Dec-Migration_Database_Version', this.version), + }, + { + type: FormItem.xtype, + _tgap: BI.isUndefined(this.version) ? vgap : 0, + name: BI.i18nText('Dec-Dcm_Connection_Form_Driver'), + value: BI.isKey(driverSource) ? `${driver} (${driverSource})` : driver, + }, + { + type: FormItem.xtype, + name: 'catalog', + invisible: database !== 'starrocks', + value: catalog, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), + value: databaseName, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + value: host, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + value: port, + }, + authType + ? { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_AuthType'), + value: authType, + } + : { + type: BI.Layout.xtype, + }, + { + type: FormItem.xtype, + name: authType ? BI.i18nText('Dec-Dcm_Connection_Form_Principal') : BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + value: authType ? principal : user, + }, + { + type: FormItem.xtype, + name: authType ? BI.i18nText('Dec-Dcm_Connection_Form_KeyPath') : BI.i18nText('Dec-Dcm_Connection_Form_Password'), + value: '******', + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_OriginalCharsetName'), + value: originalCharsetName ? originalCharsetName : BI.i18nText('Dec-Dcm_Connection_Form_Default'), + }, + // HDFS设置 + { + type: FormItem.xtype, + invisible: BI.isNull(hdfs), + name: BI.i18nText('Dec-Dcm_Connection_Address', 'HDFS'), + value: hdfs?.hdfsAddress, + }, + // 并行装载设置 + { + type: Collapse.xtype, + invisible: BI.isNull(parallelLoad), + name: BI.i18nText('Dec-Dcm_Connection_Setting', BI.i18nText('Dec-Dcm_Connection_Parallel_Load')), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.parallelLoadSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + type: BI.VerticalLayout.xtype, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.parallelLoadSet = _ref; + }, + items: [ + { + type: FormItem.xtype, + _bgap: vgap, + name: `${BI.i18nText('Dec-Dcm_Connection_Server_Address')}-${BI.i18nText('Dec-Memory_Detection_Server_Cluster_Node', '1')}`, + value: parallelLoad?.serverAddress, + }, + { + type: FormItem.xtype, + _bgap: vgap, + name: BI.i18nText('Dec-Dcm_Connection_Reuse_Temporary_Table'), + value: parallelLoad?.reuseTemporaryTable, + }, + { + type: FormItem.xtype, + _bgap: vgap, + name: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Pieces_Limit'), + value: parallelLoad?.filePiecesLimit, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Size_Limit'), + value: parallelLoad?.fileSizeLimit, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Pattern'), + value: schema, + invisible: !databaseType.hasSchema, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), + value: url, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), + value: connectionPoolAttr.maxActive, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), + value: connectionPoolAttr.testOnBorrow ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow'), + value: connectionPoolAttr.keepAlive ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_SQL_Validation_Query'), + value: api.getPlain(connectionPoolAttr.validationQuery || ''), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), + value: connectionPoolAttr.maxWait, + unit: BI.i18nText('Dec-Dcm_Millisecond'), + }, + // ssh设置 + { + type: Collapse.xtype, + width: 100, + invisible: !usingSsh, + name: BI.i18nText('Dec-Dcm_Connection_Setting', 'SSH'), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.sshSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + bgap: vgap, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.sshSet = _ref; + }, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + value: sshIp, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + value: sshPort, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + value: sshUser, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_VerifyType'), + value: CONNECT_SSH_TYPE.find((SSH_TYPE) => sshType === SSH_TYPE.value).text, + }, + { + type: FormItem.xtype, + invisible: sshType !== 'KEY', + name: BI.i18nText('Dec-Dcm_Connection_Form_PrivateKey'), + value: sshPrivateKeyPath, + }, + { + type: FormItem.xtype, + name: CONNECT_SSH_TYPE.find((SSH_TYPE) => sshType === SSH_TYPE.value).secretFormName, + value: sshSecret, + }, + ], + }, + }, + // ssl设置 + { + type: Collapse.xtype, + width: 100, + invisible: !usingSsl, + name: BI.i18nText('Dec-Dcm_Connection_Setting', 'SSL'), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.sslSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + bgap: vgap, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.sslSet = _ref; + }, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_CA_Certificate'), + value: caCertificate, + }, + { + type: FormItem.xtype, + invisible: !caCertificate, + name: BI.i18nText('Dec-Dcm_Connection_Form_Verify_CA_Certificate'), + value: verifyCa ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_SecretKey'), + value: sslClientPrivateKey, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_Certificate'), + value: sslClientCertificate, + }, + ], + }, + }, + // 更多设置 + { + type: Collapse.xtype, + width: 100, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_More_Setting'), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.advancedSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + bgap: vgap, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.advancedSet = _ref; + }, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), + value: connectionPoolAttr.initialSize, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), + value: connectionPoolAttr.minIdle, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), + value: connectionPoolAttr.testOnReturn ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), + value: connectionPoolAttr.testWhileIdle ? BI.i18nText('Dec-Dcm_Yes') : BI.i18nText('Dec-Dcm_No'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), + value: connectionPoolAttr.timeBetweenEvictionRunsMillis, + unit: BI.i18nText('Dec-Dcm_Millisecond'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), + value: connectionPoolAttr.numTestsPerEvictionRun, + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), + value: connectionPoolAttr.minEvictableIdleTimeMillis, + unit: BI.i18nText('BI-Basic_Seconds'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Evictable_Idle_Time_Millis'), + value: connectionPoolAttr.maxEvictableIdleTimeMillis, + unit: BI.i18nText('BI-Basic_Seconds'), + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow_Threshold'), + value: connectionPoolAttr.keepAliveBetweenTimeMillis, + unit: BI.i18nText('Dec-Dcm_Millisecond'), + }, + { + type: FormItem.xtype, + name: 'Fetchsize', + value: fetchSize, + }, + ], + }, + }, + ], + }; + } +} diff --git a/src/modules/pages/connection/list/list.constant.ts b/src/modules/pages/connection/list/list.constant.ts new file mode 100644 index 0000000..fa5112c --- /dev/null +++ b/src/modules/pages/connection/list/list.constant.ts @@ -0,0 +1 @@ +BI.constant('dec.constant.connection.list', []); \ No newline at end of file diff --git a/src/modules/pages/connection/list/list.model.ts b/src/modules/pages/connection/list/list.model.ts index 5d650e9..b1bc9d2 100644 --- a/src/modules/pages/connection/list/list.model.ts +++ b/src/modules/pages/connection/list/list.model.ts @@ -1,10 +1,11 @@ import { model, Model } from '@core/core'; import { ApiFactory } from '../../..//crud/apiFactory'; import { AppModel } from '../../../app.model'; +import type { ConnectionJDBC } from '../../../crud/crud.typings'; const api = new ApiFactory().create(); @model() export class ConnectionListModel extends Model<{ - types : { + types: { connections: AppModel['TYPE']['connections']; connectionSelected: AppModel['TYPE']['connectionSelected']; }, @@ -19,21 +20,40 @@ export class ConnectionListModel extends Model<{ } actions = { - setConnections: ():Promise => api.getConnectionList().then(data => { + setConnections: (): Promise => api.getConnectionList().then(data => { + data.data.push(...BI.Constants.getConstant('dec.constant.connection.list')); if (BI.size(data.data) > 0) { this.model.connections = data.data; + let defaultDatabaseName, + defaultDatabaseId = BI.Services.getService("dec.service.global") + .getHashSearchParams("databaseId"); + this.model.connections.forEach(item => { - // 后端传过来的是字符串,转为对象 - item.connectionData = JSON.parse(item.connectionData as string); + // REPORT-111534 有些环境存在脏数据,补下容错 + if (BI.isNull(item.connectionData)) return; + + // 后端传过来的是字符串,转为对象 + BI.isString(item.connectionData) && (item.connectionData = JSON.parse(item.connectionData as string)); + + // 目前只有jdbc存在identity,后期拓展 + if ((item.connectionData as ConnectionJDBC).identity === defaultDatabaseId) { + defaultDatabaseName = item.connectionName; + } }); - this.model.connectionSelected = data.data[0].connectionName; - } else { - this.model.connectionSelected = ''; + + // 仅首次进入时从url中读取参数,其他情况保留选中状态 + defaultDatabaseName ||= data.data[0].connectionName; + + this.setSelectedConnection(defaultDatabaseName); } return new Promise(resolve => { resolve(); }); }), + + setSelectedConnection(name: string) { + this.model.connectionSelected = name; + } } } diff --git a/src/modules/pages/connection/list/list.ts b/src/modules/pages/connection/list/list.ts index f674dab..8ffa81c 100644 --- a/src/modules/pages/connection/list/list.ts +++ b/src/modules/pages/connection/list/list.ts @@ -3,6 +3,7 @@ import { ListItem } from './list_item/list_item'; import { ConnectionListModel } from './list.model'; import { getDatabaseType } from './list.service'; import { Tab } from '@fui/core'; +import './list.constant' @shortcut() @store(ConnectionListModel) @@ -91,6 +92,7 @@ export class ConnectionList extends BI.Pane { creator: item.creator, databaseType: getDatabaseType(item), selected: this.model.connectionSelected ? this.model.connectionSelected === item.connectionName : index === 0, + pluginConnection: item.pluginConnection, }; }); } diff --git a/src/modules/pages/connection/list/list_item/list_item.model.ts b/src/modules/pages/connection/list/list_item/list_item.model.ts index f23de66..fa43a36 100644 --- a/src/modules/pages/connection/list/list_item/list_item.model.ts +++ b/src/modules/pages/connection/list/list_item/list_item.model.ts @@ -139,8 +139,9 @@ export class ListItemModel extends Model<{ // 后端传过来的是字符串,转为对象 item.connectionData = JSON.parse(item.connectionData as string); }); + if (name === this.model.connectionSelected) { - this.model.connectionSelected = BI.size(this.model.connections) > 0 ? this.model.connections[0].connectionName : ''; + this.setConnectionSelected(this.model.connections?.[0]?.connectionName || ''); } api.shutdownConnectionStatus(name); }); diff --git a/src/modules/pages/connection/list/list_item/list_item.ts b/src/modules/pages/connection/list/list_item/list_item.ts index 58537d7..2d1fc0f 100644 --- a/src/modules/pages/connection/list/list_item/list_item.ts +++ b/src/modules/pages/connection/list/list_item/list_item.ts @@ -6,6 +6,10 @@ import { hasRegistered } from '../list.service'; import { connectionCanEdit, getTextByDatabaseType, getChartLength } from '../../../../app.service'; import { testConnection } from '../../../maintain/forms/form.server'; import { DownListCombo, Label, SignEditor } from '@fui/core'; +import { ApiFactory } from '../../../../crud/apiFactory'; +import { checkIllegalStrings } from "@core/index"; + +const api = new ApiFactory().create(); @shortcut() @store(ListItemModel) @@ -16,6 +20,7 @@ export class ListItem extends BI.BasicButton { name: '', creator: '', databaseType: '', + pluginConnection: false, height: 25, baseCls: 'dec-dcm-connection-list-item bi-list-item-active2', $testId: 'dec-dcm-connection-list-item', @@ -39,7 +44,7 @@ export class ListItem extends BI.BasicButton { }; render() { - const { name, databaseType } = this.options; + const { name, databaseType, pluginConnection } = this.options; return { type: BI.HTapeLayout.xtype, @@ -86,6 +91,17 @@ export class ListItem extends BI.BasicButton { return; } + const result = checkIllegalStrings(newName); + if (!result.legal) { + BI.Msg.toast(result.errorMsg, { + level: 'error', + }); + this.store.setIsEdit(false, name); + this.nameLabel.setText(name); + this.nameEditor.setValue(name); + + return; + } this.store.changeName(name, newName).then(re => { this.store.setIsEdit(false, name); if (re.errorCode) { @@ -103,7 +119,7 @@ export class ListItem extends BI.BasicButton { }, ], }, { - el: databaseType ? { + el: databaseType && !pluginConnection ? { type: BI.DownListCombo.xtype, cls: 'link-item-icon', stopPropagation: true, @@ -206,8 +222,17 @@ export class ListItem extends BI.BasicButton { } private testConnectionAction() { - const { name } = this.options; - testConnection(this.model.connections.find(item => item.connectionName === name)); + // 接口返回的内容是对称加密的,前端要先解密再用新加密传回去 + const connection = BI.cloneDeep(this.model.connections + .find(item => item.connectionName === this.options.name)); + + if (BI.isNull(connection)) return; + + const validationQuery = connection?.connectionData?.connectionPoolAttr?.validationQuery || ''; + + BI.set(connection, 'connectionData.connectionPoolAttr.validationQuery', api.getCipher(api.getPlain(validationQuery))); + + testConnection(connection); } private itemActionCalculate() { diff --git a/src/modules/pages/database/database.constant.ts b/src/modules/pages/database/database.constant.ts new file mode 100644 index 0000000..12f2af8 --- /dev/null +++ b/src/modules/pages/database/database.constant.ts @@ -0,0 +1,17 @@ +import { DATEBASE_FILTER_TYPE } from "@constants/constant"; + +BI.constant('dec.constant.database.filter.type', [ + { + text: BI.i18nText('Dec-Dcm_Connection_Commonly'), + value: DATEBASE_FILTER_TYPE.COMMONLY, + selected: true, + }, + { + text: BI.i18nText('Dec-Dcm_Connection_All'), + value: DATEBASE_FILTER_TYPE.ALL, + }, + { + text: BI.i18nText('Dec-Dcm_Connection_Other'), + value: DATEBASE_FILTER_TYPE.OTHER, + }, +]); \ No newline at end of file diff --git a/src/modules/pages/database/database.model.ts b/src/modules/pages/database/database.model.ts index 44109ea..0de45eb 100644 --- a/src/modules/pages/database/database.model.ts +++ b/src/modules/pages/database/database.model.ts @@ -2,10 +2,12 @@ import { model, Model } from '@core/core'; import { AppModel } from 'src/modules/app.model'; import { getAllDatabaseTypes } from '../../app.service'; import { DatabaseType } from '../../app.typings'; +import { connectionType } from '@constants/env'; +import { OTHER_JDBC } from '@constants/constant'; @model() export class DatebaseModel extends Model<{ - types : { + types: { filter: AppModel['TYPE']['filter']; datebaseTypeSelected: AppModel['TYPE']['datebaseTypeSelected']; }, @@ -20,20 +22,30 @@ export class DatebaseModel extends Model<{ isInternal: true, isPlugin: true, datebaseTypes: getAllDatabaseTypes().filter(item => item.commonly), + isJNDILimit: false, }; } + + computed = { + otherDatabases: () => { + return this.model.isJNDILimit + ? [OTHER_JDBC] + : [OTHER_JDBC, connectionType.JNDI]; + } + } + actions = { - setSearch:(search: string) => { + setSearch: (search: string) => { this.model.search = search; }, - setFilter:(filter: string) => { + setFilter: (filter: string) => { this.model.filter = filter; }, setDatebaseTypes: (datebaseTypes: DatabaseType[]) => { this.model.datebaseTypes = datebaseTypes; this.model.datebaseTypeSelected = ''; }, - setInternal:(isInternal: boolean) => { + setInternal: (isInternal: boolean) => { this.model.isInternal = isInternal; }, setPlugin: (isPlugin: boolean) => { @@ -42,5 +54,8 @@ export class DatebaseModel extends Model<{ setDatebaseTypeSelected(datebaseType: string) { this.model.datebaseTypeSelected = datebaseType; }, + setJNDILimit: (v: boolean) => { + this.model.isJNDILimit = v; + }, } } diff --git a/src/modules/pages/database/database.ts b/src/modules/pages/database/database.ts index 93b32ae..85a4a3b 100644 --- a/src/modules/pages/database/database.ts +++ b/src/modules/pages/database/database.ts @@ -6,6 +6,11 @@ import { connectionType } from '@constants/env'; import { DatebaseType } from './database_type/database_type'; import { getAllDatabaseTypes } from '../../app.service'; import { ButtonGroup, MultiSelectItem, SearchEditor } from '@fui/core'; +import { ApiFactory } from 'src/modules/crud/apiFactory'; +import './database.constant' + +const api = new ApiFactory().create(); + @shortcut() @store(DatebaseModel) export class Datebase extends BI.Widget { @@ -65,6 +70,7 @@ export class Datebase extends BI.Widget { mounted() { this.store.setFilter(DATEBASE_FILTER_TYPE.COMMONLY); this.store.setDatebaseTypeSelected(''); + this.getDatabaseTypeLimit(); } render() { @@ -116,24 +122,12 @@ export class Datebase extends BI.Widget { ref: (_ref: ButtonGroup) => { this.filter = _ref; }, - items: [ - { + items: () => BI.map(BI.Constants.getConstant('dec.constant.database.filter.type'), (_, value) => { + return { type: Filter.xtype, - text: BI.i18nText('Dec-Dcm_Connection_Commonly'), - value: DATEBASE_FILTER_TYPE.COMMONLY, - selected: true, - }, - { - type: Filter.xtype, - text: BI.i18nText('Dec-Dcm_Connection_All'), - value: DATEBASE_FILTER_TYPE.ALL, - }, - { - type: Filter.xtype, - text: BI.i18nText('Dec-Dcm_Connection_Other'), - value: DATEBASE_FILTER_TYPE.OTHER, - }, - ], + ...value, + } + }), }, width: 200, }, @@ -142,20 +136,18 @@ export class Datebase extends BI.Widget { items: [ { el: { - type: BI.HTapeLayout.xtype, + type: BI.VerticalAdaptLayout.xtype, hgap: 20, invisible: true, items: [ { type: BI.Label.xtype, - width: 70, textAlign: 'left', text: BI.i18nText('Dec-Dcm_Connection_Type_Filter'), title: BI.i18nText('Dec-Dcm_Connection_Type_Filter'), }, { type: BI.MultiSelectItem.xtype, - width: 80, selected: this.model.isInternal, text: BI.i18nText('Dec-Dcm_Connection_Support_Inner'), title: BI.i18nText('Dec-Dcm_Connection_Support_Inner'), @@ -168,7 +160,6 @@ export class Datebase extends BI.Widget { }, { type: BI.MultiSelectItem.xtype, - width: 80, selected: this.model.isPlugin, text: BI.i18nText('Dec-Dcm_Connection_Support_Plugin'), title: BI.i18nText('Dec-Dcm_Connection_Support_Plugin'), @@ -250,11 +241,20 @@ export class Datebase extends BI.Widget { this.store.setDatebaseTypes(this.allDatabaseTypes.filter(item => item.commonly)); break; case DATEBASE_FILTER_TYPE.OTHER: - this.store.setDatebaseTypes(this.allDatabaseTypes.filter(item => item.type === connectionType.JNDI || item.type === OTHER_JDBC)); + this.store.setDatebaseTypes(this.allDatabaseTypes.filter(item => this.model.otherDatabases.includes(item.type))); break; - default: + case DATEBASE_FILTER_TYPE.ALL: this.store.setDatebaseTypes(this.allDatabaseTypes.filter(item => item.type !== connectionType.JNDI && item.type !== OTHER_JDBC)); break; + default: + this.store.setDatebaseTypes(this.allDatabaseTypes.filter(item => item.marker && (item.marker === filter))); + break; } } + + // 获取JNDI + private async getDatabaseTypeLimit() { + const result = await api.getJNDIDatabaseStatus(); + this.store.setJNDILimit(result.data); + } } diff --git a/src/modules/pages/database/database_type/database_type.model.ts b/src/modules/pages/database/database_type/database_type.model.ts index cb51507..483b700 100644 --- a/src/modules/pages/database/database_type/database_type.model.ts +++ b/src/modules/pages/database/database_type/database_type.model.ts @@ -6,11 +6,13 @@ export class DatebaseTypeModel extends Model< types: { datebaseTypeSelected: AppModel['TYPE']['datebaseTypeSelected']; pageIndex: AppModel['TYPE']['pageIndex']; + noTestConnection: AppModel['TYPE']['noTestConnection']; + datebaseTypeSelectedOne: AppModel['TYPE']['datebaseTypeSelectedOne']; }, context: DatebaseTypeModel['context']; }> { static xtype = 'dec.dcm.model.datebase.type'; - context = ['datebaseTypeSelected', 'pageIndex']; + context = ['datebaseTypeSelected', 'pageIndex', 'noTestConnection', 'datebaseTypeSelectedOne']; actions = { setDatebaseTypeSelected: (datebaseTypeSelected: string) => { @@ -19,5 +21,8 @@ export class DatebaseTypeModel extends Model< setPageIndex: (index: string) => { this.model.pageIndex = index; }, + setNoTestConnection: (value: boolean) => { + this.model.noTestConnection = value; + } } } diff --git a/src/modules/pages/database/database_type/database_type.ts b/src/modules/pages/database/database_type/database_type.ts index 6778564..2741408 100644 --- a/src/modules/pages/database/database_type/database_type.ts +++ b/src/modules/pages/database/database_type/database_type.ts @@ -70,6 +70,7 @@ export class DatebaseType extends BI.BasicButton { const { value } = this.options; this.store.setDatebaseTypeSelected(value); this.store.setPageIndex(PAGE_INDEX.MAINTAIN); + this.store.setNoTestConnection(this.model.datebaseTypeSelectedOne.isHideConnection); } mounted() { @@ -88,6 +89,9 @@ export class DatebaseType extends BI.BasicButton { if (url.startsWith('/')) { return `${PluginImgPrefix}${url}`; } + if (url.startsWith('http')) { + return url; + } return `${PluginImgPrefix}/${url}`; } diff --git a/src/modules/pages/maintain/components/driverselector/driverselector.model.ts b/src/modules/pages/maintain/components/driverselector/driverselector.model.ts index 384e4f4..15b164e 100644 --- a/src/modules/pages/maintain/components/driverselector/driverselector.model.ts +++ b/src/modules/pages/maintain/components/driverselector/driverselector.model.ts @@ -67,7 +67,8 @@ export class DriverSelectorModel extends Model { this.model.customDriver.driver = driver; this.model.customDrivers.some(customDriver => { - if (customDriver.driverClass === driver) { + // DEC-21469 存在driver值相同但driver名不同的场景,因此要用拼接名判断 + if (`${customDriver.driverClass} (${customDriver.name})` === value) { this.model.driverSource = customDriver.name; this.model.customDriver.value = `${driver} (${customDriver.name})`; @@ -86,6 +87,12 @@ export class DriverSelectorModel extends Model { changeDriverSource: driverTypeComboValue => { this.model.driverSource = driverTypeComboValue === 'default' ? '' : this.model.driverSource; }, + + setDefaultDrivers: version => { + const defaultDrivers = this.getDrivers(version); + this.model.defaultDrivers = defaultDrivers; + this.changeDefaultDriver(defaultDrivers[0]?.value); + } }; private resolveSelectedDriverType = () => { @@ -96,11 +103,12 @@ export class DriverSelectorModel extends Model { return [this.options.driverSource, this.options.driver]; }; - private getDrivers = () => { + private getDrivers = (version?: string) => { const connectionData = this.options.connectionData as ConnectionJDBC; const connectionType = getJdbcDatabaseType(connectionData.database, connectionData.driver); + const selectedVersion = version ?? this.options.version; const drivers = connectionType.drivers ? - connectionType.drivers.map(item => { + (BI.isUndefined(connectionType.versions) ? connectionType.drivers : connectionType.drivers[selectedVersion]).map(item => { return { text: item, value: item, @@ -110,8 +118,7 @@ export class DriverSelectorModel extends Model { text: connectionType.driver, value: connectionType.driver, }]; - - if (!drivers.some(item => item.text === connectionData.driver)) { + if (BI.isUndefined(connectionType.versions) && !drivers.some(item => item.text === connectionData.driver)) { return [ { text: connectionData.driver, diff --git a/src/modules/pages/maintain/components/driverselector/driverselector.ts b/src/modules/pages/maintain/components/driverselector/driverselector.ts index ef6c351..8408e05 100644 --- a/src/modules/pages/maintain/components/driverselector/driverselector.ts +++ b/src/modules/pages/maintain/components/driverselector/driverselector.ts @@ -24,21 +24,32 @@ export class DriverSelector extends BI.Widget { driver: '', driverSource: '', connectionData: {} as ConnectionJDBC, + version: '', }; defaultDrivers: EditorIconCheckCombo = null; customDrivers: SearchTextValueCombo = null; + beforeRender(cb: Function) { + this.store.initDriverClassList(cb); + } + watch = { driverClassItems: items => { - // this.customDrivers.populate(items); - // this.customDrivers.setValue(this.model.customDriver.value); + this.customDrivers.populate(items); + this.customDrivers.setValue(this.model.customDriver.value); }, driverManageEntryVisible: b => { - this.driverManageEntry.setVisible(false); + this.driverManageEntry.setVisible(b); }, + + defaultDrivers: items => { + this.defaultDrivers.populate(items); + this.defaultDrivers.setValue(this.model.defaultDriver.driver); + this.fireEvent('EVENT_CHANGE'); + } }; private driverManageEntry = null; @@ -55,7 +66,6 @@ export class DriverSelector extends BI.Widget { type: BI.TextValueCombo.xtype, width: 86, value: this.model.selectedDriverType, - invisible: true, items: [ { text: BI.i18nText('Dec-Basic_Default'), @@ -95,7 +105,7 @@ export class DriverSelector extends BI.Widget { this.defaultDrivers = _ref; }, invisible: this.model.driverSource !== '', - width: 300, + width: 204, items: this.model.defaultDrivers, value: this.model.defaultDriver.driver, listeners: [ @@ -116,13 +126,13 @@ export class DriverSelector extends BI.Widget { ref: _ref => { this.customDrivers = _ref; }, - invisible: true, + invisible: this.model.driverSource === '', width: 204, watermark: BI.i18nText('Dec-Please_Input'), items: this.model.driverClassItems, value: this.model.customDriver.value, text: () => this.model.customDriver.value || '', - defaultText: BI.i18nText('Dec-Please_Select'), + defaultText: BI.i18nText('Dec-Please_Select_One'), warningTitle: BI.i18nText('Dec-Dcm-Driver_Driver_File_Lost'), listeners: [ { @@ -146,7 +156,7 @@ export class DriverSelector extends BI.Widget { text: BI.i18nText('Dec-Dcm_Create_New_Driver'), }, from: '.dec-dcm', - invisible: true, + invisible: !this.model.driverManageEntryVisible, listeners: [ { eventName: 'EVENT_CLOSE', @@ -175,8 +185,12 @@ export class DriverSelector extends BI.Widget { getValue() { return { - // driverSource: this.model.driverSource, + driverSource: this.model.driverSource, driver: this.model.driverSource === '' ? this.model.defaultDriver.driver : this.model.customDriver.driver, }; } + + setDefaultDrivers(version: string) { + this.store.setDefaultDrivers(version); + } } diff --git a/src/modules/pages/maintain/components/form_item/form_item.ts b/src/modules/pages/maintain/components/form_item/form_item.ts index 00b6f1a..a0d54eb 100644 --- a/src/modules/pages/maintain/components/form_item/form_item.ts +++ b/src/modules/pages/maintain/components/form_item/form_item.ts @@ -1,4 +1,5 @@ import { shortcut } from '@core/core'; +import { Label } from '@fui/core'; import { CONNECTION_LAYOUT } from '@constants/constant'; @shortcut() @@ -11,14 +12,19 @@ export class FormItem extends BI.Widget { nameWidth: 140, isBold: true, $testId: 'dec-dcm-maintain-form-item', - } + }; + + nameLabel: Label; - render () { + render() { return { type: BI.FloatLeftLayout.xtype, items: [ { type: BI.Label.xtype, + ref: (ref: Label) => { + this.nameLabel = ref; + }, cls: this.options.isBold ? 'bi-font-bold' : '', width: this.options.nameWidth, textAlign: 'left', @@ -29,4 +35,12 @@ export class FormItem extends BI.Widget { ], }; } + + /** + * 设置表单名称 + * @param name + */ + setName(name: string) { + this.nameLabel.setText(name); + } } diff --git a/src/modules/pages/maintain/forms/components/form.jdbc.ts b/src/modules/pages/maintain/forms/components/form.jdbc.ts index 34ffe27..430a7aa 100644 --- a/src/modules/pages/maintain/forms/components/form.jdbc.ts +++ b/src/modules/pages/maintain/forms/components/form.jdbc.ts @@ -1,879 +1,1876 @@ -import { shortcut } from '@core/core'; -import { Collapse, EVENT_CHANGE } from 'src/modules/components/collapse/collapse'; -import { FormItem } from '../../components/form_item/form_item'; -import { Connection, ConnectionJDBC, ConnectionPoolJDBC } from 'src/modules/crud/crud.typings'; -import { connectionType } from '@constants/env'; -import { CONNECT_CHARSET, CONNECTION_LAYOUT, INT_MAX_VALUE, INT_MIN_VALUE } from '@constants/constant'; -import { getAllDatabaseTypes, getJdbcDatabaseType, resolveUrlInfo, splitUrl } from '../../../../app.service'; -import { TextChecker } from '../../../../components/text_checker/text_checker'; -import { ApiFactory } from 'src/modules/crud/apiFactory'; -import { - Editor, - EditorIconCheckCombo, - Label, - TextAreaEditor, - TextEditor, - TextValueCombo, - VerticalLayout, -} from '@fui/core'; -import { DriverSelector } from '../../components/driverselector/driverselector'; - -const api = new ApiFactory().create(); - -@shortcut() -export class FormJdbc extends BI.Widget { - static xtype = 'dec.dcm.maintain.form.jdbc'; - - props = { - formData: {} as Connection, - }; - - oldPassword = ''; - allDatabaseTypes = getAllDatabaseTypes(); - - advancedSet: VerticalLayout; - formUser: FormItem; - formPassword: FormItem; - formPrincipal: FormItem; - formKeyPath: FormItem; - labelTips: Label; - - form = { - connectionName: null, - driver: null, - database: null, - host: null, - port: null, - user: null, - password: null, - authType: null, - principal: null, - keyPath: null, - originalCharsetName: null, - schema: null, - url: null, - initialSize: null, - maxActive: null, - maxIdle: null, - maxWait: null, - validationQuery: null, - testOnBorrow: null, - testOnReturn: null, - testWhileIdle: null, - timeBetweenEvictionRunsMillis: null, - numTestsPerEvictionRun: null, - minIdle: null, - minEvictableIdleTimeMillis: null, - fetchSize: null, - }; - - render() { - const { connectionName, connectionData } = this.options.formData; - const { - driver, - driverSource, - user, - password, - originalCharsetName, - schema, - url, - connectionPoolAttr, - database, - authType, - principal, - keyPath, - fetchSize, - } = connectionData as ConnectionJDBC; - const { - initialSize, - maxActive, - maxIdle, - maxWait, - validationQuery, - testOnBorrow, - testOnReturn, - testWhileIdle, - timeBetweenEvictionRunsMillis, - numTestsPerEvictionRun, - minIdle, - minEvictableIdleTimeMillis, - } = connectionPoolAttr as ConnectionPoolJDBC; - const databaseType = getJdbcDatabaseType(database, driver); - this.oldPassword = password; - const { host, port, databaseName } = resolveUrlInfo(url, database); - const { hgap, vgap } = CONNECTION_LAYOUT; - - const valueRangeConfig = { - errorText: BI.i18nText('Dec-Dcm_Connection_Value_Out_Range'), - checker: (value: string) => this.checkValueRange(value), - autoFix: true, - }; - - return { - type: BI.VerticalLayout.xtype, - hgap, - vgap, - items: [ - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Name'), - forms: [{ - type: TextChecker.xtype, - $value: 'connection-name', - width: 300, - value: connectionName, - allowBlank: true, - ref: (_ref: TextChecker) => { - this.form.connectionName = _ref; - }, - watermark: BI.i18nText('Dec-Dcm_Data_Connections'), - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Driver'), - forms: [ - { - type: DriverSelector.xtype, - ref: (_ref: DriverSelector) => { - this.form.driver = _ref; - }, - driver, - driverSource, - connectionData, - listeners: [ - { - eventName: 'EVENT_CHANGE', - action: () => { - const value = this.form.driver.getValue(); - const connectionData = this.options.formData.connectionData as ConnectionJDBC; - const connectionType = getJdbcDatabaseType(connectionData.database, connectionData.driver); - // DEC-2020 - const url = (connectionType.urls && connectionType.urls[value.driver]) || connectionType.url; - this.form.url.setValue(url); - const urlInfo = resolveUrlInfo(url, connectionData.database); - this.form.host.setValue(urlInfo.host); - this.form.database.setValue(urlInfo.databaseName); - this.form.port.setValue(urlInfo.port); - }, - }, - ], - }, - ], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), - forms: [{ - type: BI.TextEditor.xtype, - $value: 'database-name', - width: 300, - allowBlank: true, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), - value: databaseName, - ref: (_ref: any) => { - this.form.database = _ref; - }, - listeners: [{ - eventName: BI.Editor.EVENT_CHANGE, - action: () => { - this.onHostPortChange(databaseType); - }, - }], - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), - forms: [{ - type: BI.TextEditor.xtype, - $value: 'database-host', - width: 300, - allowBlank: true, - value: host, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Host'), - ref: (_ref: any) => { - this.form.host = _ref; - }, - listeners: [{ - eventName: BI.Editor.EVENT_CHANGE, - action: () => { - this.onHostPortChange(databaseType); - }, - }], - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), - forms: [{ - type: TextChecker.xtype, - $value: 'database-port', - width: 300, - allowBlank: true, - value: port, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.port = _ref; - }, - listeners: [{ - eventName: BI.Editor.EVENT_CHANGE, - action: () => { - this.onHostPortChange(databaseType); - }, - }], - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_AuthType'), - invisible: !databaseType.kerberos, - forms: [{ - type: BI.TextValueCombo.xtype, - $value: 'auth-type', - width: 300, - value: authType, - ref: (_ref: TextValueCombo) => { - this.form.authType = _ref; - }, - items: [ - { - text: BI.i18nText('Dec-Dcm_Connection_Form_UserName_Password'), - value: '', - }, - { - text: 'Kerberos', - value: 'kerberos', - }, - ], - listeners: [ - { - eventName: BI.Combo.EVENT_CHANGE, - action: () => { - const type = this.form.authType.getValue()[0]; - this.formPrincipal.setVisible(!!type); - this.formKeyPath.setVisible(!!type); - this.formUser.setVisible(!type); - this.formPassword.setVisible(!type); - this.labelTips.setVisible(!!type); - }, - }, - ], - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), - invisible: !!authType, - ref: (_ref: FormItem) => { - this.formUser = _ref; - }, - forms: [{ - type: BI.TextEditor.xtype, - $value: 'username', - width: 300, - allowBlank: true, - value: user, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), - ref: (_ref: TextEditor) => { - this.form.user = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Password'), - invisible: !!authType, - ref: (_ref: FormItem) => { - this.formPassword = _ref; - }, - forms: [{ - type: BI.Editor.xtype, - $value: 'password', - cls: 'bi-border-bottom', - width: 300, - height: 20, - allowBlank: true, - value: password, - inputType: 'password', - autocomplete: 'new-password', - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Password'), - ref: (_ref: Editor) => { - this.form.password = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Principal'), - invisible: !authType, - ref: (_ref: FormItem) => { - this.formPrincipal = _ref; - }, - forms: [{ - type: BI.TextEditor.xtype, - $value: 'principal', - width: 300, - allowBlank: true, - value: principal, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Principal'), - ref: (_ref: TextEditor) => { - this.form.principal = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_KeyPath'), - invisible: !authType, - ref: (_ref: FormItem) => { - this.formKeyPath = _ref; - }, - forms: [{ - type: BI.Editor.xtype, - $value: 'key-path', - cls: 'bi-border', - width: 300, - height: 20, - allowBlank: true, - value: keyPath, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_KeyPath'), - ref: (_ref: Editor) => { - this.form.keyPath = _ref; - }, - }], - }, - { - type: BI.Label.xtype, - cls: 'bi-tips', - textAlign: 'left', - invisible: true, - text: BI.i18nText('Dec-Dcm_Connection_JDBC_Warning'), - ref: (_ref: Label) => { - this.labelTips = _ref; - }, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_OriginalCharsetName'), - forms: [{ - type: BI.TextValueCombo.xtype, - $value: 'original-charset-name', - width: 300, - value: originalCharsetName ? originalCharsetName : '', - items: CONNECT_CHARSET, - ref: (_ref: TextValueCombo) => { - this.form.originalCharsetName = _ref; - }, - }], - }, - { - type: FormItem.xtype, - invisible: !databaseType.hasSchema, - height: 64, - name: BI.i18nText('Dec-Dcm_Connection_Form_Pattern'), - forms: [{ - type: BI.VerticalLayout.xtype, - items: [{ - type: BI.FloatLeftLayout.xtype, - items: [{ - type: BI.TextButton.xtype, - cls: 'bi-high-light', - text: BI.i18nText('Dec-Dcm_Connection_Click_Connect_Database'), - handler: () => { - this.fireEvent('EVENT_TEST_CONNECTION'); - }, - }, { - type: BI.Label.xtype, - cls: 'bi-tips', - lgap: 3, - text: BI.i18nText('Dec-Dcm_Connection_Read_Mode_List'), - }], - }, { - type: BI.TextValueCombo.xtype, - $value: 'schema', - width: 300, - vgap: 15, - disabled: true, - value: schema, - items: schema ? [{ text: schema, value: schema }] : [], - ref: (_ref: TextValueCombo) => { - this.form.schema = _ref; - }, - }], - }], - }, - { - type: BI.Layout.xtype, - cls: 'bi-border-top', - bgap: 8, - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), - forms: [{ - type: BI.TextEditor.xtype, - $value: 'database-url', - width: 300, - allowBlank: true, - value: url, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), - ref: (_ref: TextEditor) => { - this.form.url = _ref; - }, - listeners: [{ - eventName: 'EVENT_CHANGE', - action: () => { - const urlInfo = resolveUrlInfo(this.form.url.getValue(), database); - this.form.host.setValue(urlInfo.host); - this.form.database.setValue(urlInfo.databaseName); - this.form.port.setValue(urlInfo.port); - }, - }], - }], - }, - { - type: Collapse.xtype, - bgap: -15, - width: 70, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Advanced_Setting'), - listeners: [ - { - eventName: EVENT_CHANGE, - action: (isCollapse: boolean) => { - this.advancedSet.setVisible(!isCollapse); - }, - }, - ], - }, - { - type: BI.VerticalLayout.xtype, - vgap, - tgap: -15, - invisible: true, - ref: (_ref: VerticalLayout) => { - this.advancedSet = _ref; - }, - items: [ - { - type: FormItem.xtype, - tgap: 15, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), - forms: [{ - type: TextChecker.xtype, - $value: 'initial-size', - width: 300, - allowBlank: false, - value: initialSize, - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), - ref: (_ref: TextChecker) => { - this.form.initialSize = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), - forms: [{ - type: TextChecker.xtype, - $value: 'max-active', - width: 300, - allowBlank: false, - value: maxActive, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.maxActive = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Idle'), - forms: [{ - type: TextChecker.xtype, - $value: 'max-idle', - width: 300, - allowBlank: false, - value: maxIdle, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Idle'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.maxIdle = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), - forms: [{ - type: TextChecker.xtype, - $value: 'min-idle', - width: 300, - allowBlank: false, - value: minIdle, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.minIdle = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), - forms: [ - { - type: TextChecker.xtype, - $value: 'max-wait', - width: 300, - allowBlank: false, - value: maxWait, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.maxWait = _ref; - }, - }, - { - type: BI.Label.xtype, - lgap: 5, - height: CONNECTION_LAYOUT.labelHeight, - text: BI.i18nText('Dec-Dcm_Millisecond'), - }, - ], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_SQL_Validation_Query'), - forms: [{ - type: BI.TextAreaEditor.xtype, - $value: 'validation-query', - cls: 'bi-border', - allowBlank: true, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Place_Input'), - value: api.getPlain(validationQuery || ''), - width: 300, - height: 100, - ref: (_ref: TextAreaEditor) => { - this.form.validationQuery = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), - forms: [{ - type: BI.TextValueCombo.xtype, - $value: 'check', - width: 300, - allowBlank: true, - value: testOnBorrow, - items: this.getBooleanItem(), - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), - ref: (_ref: TextValueCombo) => { - this.form.testOnBorrow = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), - forms: [{ - type: BI.TextValueCombo.xtype, - $value: 'test-on-return', - width: 300, - allowBlank: true, - value: testOnReturn, - items: this.getBooleanItem(), - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), - ref: (_ref: TextValueCombo) => { - this.form.testOnReturn = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), - forms: [{ - type: BI.TextValueCombo.xtype, - $value: 'test-while-idle', - width: 300, - allowBlank: true, - value: testWhileIdle, - items: this.getBooleanItem(), - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), - ref: (_ref: TextValueCombo) => { - this.form.testWhileIdle = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), - forms: [ - { - type: TextChecker.xtype, - $value: 'test-between-evicition-millis', - width: 300, - allowBlank: false, - value: timeBetweenEvictionRunsMillis, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Number'), - checker: (value: string) => this.checkNumber(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.timeBetweenEvictionRunsMillis = _ref; - }, - }, - { - type: BI.Label.xtype, - lgap: 5, - height: CONNECTION_LAYOUT.labelHeight, - text: BI.i18nText('Dec-Dcm_Millisecond'), - }, - ], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), - forms: [{ - type: TextChecker.xtype, - $value: 'test-pereviction-run-num', - width: 300, - allowBlank: false, - value: numTestsPerEvictionRun, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.numTestsPerEvictionRun = _ref; - }, - }], - }, - { - type: FormItem.xtype, - name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), - forms: [ - { - type: TextChecker.xtype, - $value: 'min-evictable-idle-time-millis', - width: 300, - allowBlank: false, - value: minEvictableIdleTimeMillis, - watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer'), - checker: (value: string) => this.checkInteger(value), - autoFix: true, - }, valueRangeConfig], - ref: (_ref: TextChecker) => { - this.form.minEvictableIdleTimeMillis = _ref; - }, - }, - { - type: BI.Label.xtype, - lgap: 5, - height: CONNECTION_LAYOUT.labelHeight, - text: BI.i18nText('BI-Basic_Seconds'), - }, - ], - }, { - el: { - type: BI.VerticalLayout.xtype, - cls: 'bi-border-top', - invisible: fetchSize < 0 && fetchSize !== -2, - items: [ - { - el: { - type: FormItem.xtype, - name: 'Fetchsize', - forms: [{ - type: TextChecker.xtype, - $value: 'fetch-size', - width: 300, - allowBlank: true, - value: fetchSize === -2 ? '' : fetchSize, - watermark: 'Fetchsize', - validationChecker: [{ - errorText: BI.i18nText('Dec-Dcm_Connection_Check_Fetch_Size_Range'), - checker: (value: string) => BI.isInteger(value) && BI.parseInt(value) >= 0 && BI.parseInt(value) <= 1000000, - autoFix: true, - }], - ref: (_ref: TextChecker) => { - this.form.fetchSize = _ref; - }, - }], - }, - vgap: 15, - }, - ], - }, - }, - ], - }, - ], - }; - } - - public setError(value: string) { - this.form.connectionName.setError(value); - } - - private checkInteger(value: string) { - return /^[\d]+$/.test(value); - } - - private checkNumber(value: string) { - return /^[(\-|\+)?\d]+$/.test(value); - } - - private checkValueRange(value: string) { - return parseInt(value, 0) <= INT_MAX_VALUE && parseInt(value, 0) >= INT_MIN_VALUE; - } - - private getDrivers() { - const connectionData = this.options.formData.connectionData as ConnectionJDBC; - const connectionType = getJdbcDatabaseType(connectionData.database, connectionData.driver); - const drivers = connectionType.drivers ? - connectionType.drivers.map(item => { - return { - text: item, - value: item, - }; - }) : - [{ - text: connectionType.driver, - value: connectionType.driver, - }]; - - if (!drivers.some(item => item.text === connectionData.driver)) { - return [ - { - text: connectionData.driver, - value: connectionData.driver, - }, - ...drivers, - ]; - } - - return drivers; - } - - private getBooleanItem() { - return [ - { - text: BI.i18nText('Dec-Dcm_Yes'), - value: true, - }, - { - text: BI.i18nText('Dec-Dcm_No'), - value: false, - }, - ]; - } - - private onHostPortChange(databaseType) { - const { urls, url } = databaseType; - const driver = this.form.driver.getValue(); - const selectUrl = BI.get(urls, driver.driver) || url; - const host = this.form.host.getValue(); - const port = this.form.port.getValue(); - const database = this.form.database.getValue(); - this.form.url.setValue(splitUrl(host, port, database, selectUrl)); - } - - public setSchemas(schemas: string[]) { - this.form.schema.setEnable(true); - if (schemas.length > 0) { - const value = this.form.schema.getValue()[0]; - this.form.schema.populate(schemas.map(item => { - return { - text: item, - value: item, - }; - })); - this.form.schema.setValue(value && schemas.some(item => item === value) ? value : schemas[0]); - } - } - - public validation(): boolean { - return this.form.driver.validation(); - } - - public getSubmitValue(): Connection { - const connectionData = this.options.formData.connectionData as ConnectionJDBC; - const connectionPoolAttr = connectionData.connectionPoolAttr; - const originalCharsetName = this.form.originalCharsetName.getValue()[0] || ''; - // TODO 获取表单数据这里待优化 - - return { - connectionType: connectionType.JDBC, - connectionId: this.form.connectionName.getValue(), - connectionName: this.form.connectionName.getValue(), - connectionData: BI.extend({}, connectionData, { - database: connectionData.database, - connectionName: this.form.connectionName.getValue(), - ...this.form.driver.getValue(), - url: this.form.url.getValue(), - user: this.form.user.getValue(), - password: this.oldPassword === this.form.password.getValue() ? this.oldPassword : api.getCipher(this.form.password.getValue()), - queryType: '', - newCharsetName: originalCharsetName ? 'gbk' : '', // 后台要求,originalCharsetName不为空时,newCharsetName为gbk - originalCharsetName, - schema: this.form.schema.getValue()[0], - host: this.form.host.getValue(), - authType: this.form.authType.getValue()[0] || '', - creator: Dec ? Dec.personal.username : '', - principal: this.form.principal.getValue(), - keyPath: this.form.keyPath.getValue(), - fetchSize: BI.isEmptyString(this.form.fetchSize.getValue()) ? -2 : BI.parseInt(this.form.fetchSize.getValue()), - connectionPoolAttr: { - initialSize: this.form.initialSize.getValue(), - maxActive: this.form.maxActive.getValue(), - maxIdle: this.form.maxIdle.getValue(), - minIdle: this.form.minIdle.getValue(), - maxWait: this.form.maxWait.getValue(), - validationQuery: api.getCipher(this.form.validationQuery.getValue()), - testOnBorrow: BI.size(this.form.testOnBorrow.getValue()) > 0 ? this.form.testOnBorrow.getValue()[0] : connectionPoolAttr.testOnBorrow, - testOnReturn: BI.size(this.form.testOnReturn.getValue()) > 0 ? this.form.testOnReturn.getValue()[0] : connectionPoolAttr.testOnReturn, - testWhileIdle: BI.size(this.form.testWhileIdle.getValue()) > 0 ? this.form.testWhileIdle.getValue()[0] : connectionPoolAttr.testWhileIdle, - timeBetweenEvictionRunsMillis: this.form.timeBetweenEvictionRunsMillis.getValue(), - numTestsPerEvictionRun: this.form.numTestsPerEvictionRun.getValue(), - minEvictableIdleTimeMillis: this.form.minEvictableIdleTimeMillis.getValue(), - }, - }), - }; - } -} +import { shortcut } from '@core/core'; +import { Collapse, EVENT_CHANGE } from 'src/modules/components/collapse/collapse'; +import { FormItem } from '../../components/form_item/form_item'; +import { Connection, ConnectionJDBC, ConnectionPoolJDBC } from 'src/modules/crud/crud.typings'; +import { connectionType } from '@constants/env'; +import { CONNECT_CHARSET, CONNECTION_LAYOUT, INT_MAX_VALUE, INT_MIN_VALUE, CONNECT_SSH_TYPE, CONNECT_SSL_TYPE, YES_OR_NO } from '@constants/constant'; +import { getAllDatabaseTypes, getJdbcDatabaseType, resolveUrlInfo, splitUrl } from '../../../../app.service'; +import { DatabaseType } from 'src/modules/app.typings'; +import { TextChecker } from '../../../../components/text_checker/text_checker'; +import { FileChooser } from '../../../../components/file_chooser/file_chooser'; +import { ApiFactory } from 'src/modules/crud/apiFactory'; +import { Editor, Label, TextAreaEditor, TextEditor, TextValueCombo, VerticalLayout, MultiSelectItem } from '@fui/core'; +import { DriverSelector } from '../../components/driverselector/driverselector'; +import { FileUpload } from '../../../../components/file_upload/file_upload'; +import { TipsCombo } from '../../../../components/tips_combo/tips_combo'; + +const api = new ApiFactory().create(); + +const EDITOR_WIDTH = 300, EDITOR_HEIGHT = 20; + +@shortcut() +export class FormJdbc extends BI.Widget { + static xtype = 'dec.dcm.maintain.form.jdbc'; + + props = { + formData: {} as Connection, + }; + + oldPassword = ''; + oldSshSecret = ''; + + databaseType: DatabaseType; + allDatabaseTypes = getAllDatabaseTypes(); + + parallelLoadSet: VerticalLayout; + hdfsSet: VerticalLayout; + sshSet: VerticalLayout; + sshForm: VerticalLayout; + sslSet: VerticalLayout; + sslForm: VerticalLayout; + advancedSet: VerticalLayout; + formUser: FormItem; + formPassword: FormItem; + formPrincipal: FormItem; + formKeyPath: FormItem; + formKrb5File: FormItem; + labelTips: Label; + schemaForm: FormItem; + + form = { + connectionName: null, + version: null, + driver: null, + catalog: null, + database: null, + host: null, + port: null, + user: null, + password: null, + authType: null, + principal: null, + keyPath: null, + krb5Path: null, + originalCharsetName: null, + schema: null, + url: null, + initialSize: null, + maxActive: null, + maxWait: null, + // ssh + usingSsh: null, + sshIp: null, + sshPort: null, + sshUser: null, + sshType: null, + sshPrivateKeyPathForm: null, + sshPrivateKeyPath: null, + sshSecretForm: null, + sshSecret: null, + // ssl + usingSsl: null, + caCertificate: null, + verifyCa: null, + sslClientPrivateKey: null, + sslClientCertificate: null, + // more + validationQuery: null, + testOnBorrow: null, + keepAlive: null, + testOnReturn: null, + testWhileIdle: null, + timeBetweenEvictionRunsMillis: null, + numTestsPerEvictionRun: null, + minIdle: null, + minEvictableIdleTimeMillis: null, + maxEvictableIdleTimeMillis: null, + keepAliveBetweenTimeMillis: null, + fetchSize: null, + // 并行装载 + parallelLoad: { + serverAddress: '', + isReuseTemporaryTable: 0, + filePiecesLimit: null, + fileSizeLimit: null, + }, + // HDFS + hdfs: { + hdfsAddress: null, + } + }; + + render() { + const { connectionName, connectionData } = this.options.formData; + const { + driver, + driverSource, + user, + password, + originalCharsetName, + schema, + url, + connectionPoolAttr, + database, + authType, + principal, + keyPath, + krb5Path, + fetchSize, + // ssh + usingSsh = false, + sshIp, // sshIp默认值在表单那用||设置,因为在这可能是空字符串,解构默认值没用 + sshPort = 22, + sshUser = '', + sshType = CONNECT_SSH_TYPE[0].value, + sshPrivateKeyPath = '', + sshSecret = '', + // ssl + usingSsl = false, + caCertificate = '', + verifyCa = false, + sslClientPrivateKey = '', + sslClientCertificate = '', + // 并行装载 + parallelLoad, + // HDFS + hdfs, + } = connectionData as ConnectionJDBC; + this.oldPassword = password; + this.oldSshSecret = sshSecret; + !BI.isUndefined(principal) && this.initPrincipals(keyPath, principal); + + const { + initialSize, + maxActive, + maxWait, + validationQuery, + testOnBorrow, + keepAlive, + testOnReturn, + testWhileIdle, + timeBetweenEvictionRunsMillis, + numTestsPerEvictionRun, + minIdle, + minEvictableIdleTimeMillis, + maxEvictableIdleTimeMillis, + keepAliveBetweenTimeMillis, + } = connectionPoolAttr as ConnectionPoolJDBC; + const databaseType = getJdbcDatabaseType(database, driver); + this.databaseType = databaseType; + const { host, port, catalog, databaseName, version } = resolveUrlInfo(url, database); + this.version = !BI.isUndefined(databaseType.versions) ? (version ?? databaseType.versions[0]) : version; + const { hgap, vgap } = CONNECTION_LAYOUT; + + const valueRangeConfig = { + errorText: BI.i18nText('Dec-Dcm_Connection_Value_Out_Range'), + checker: (value: string) => this.checkValueRange(value), + autoFix: true, + }; + + return { + type: BI.VerticalLayout.xtype, + hgap, + vgap, + items: [ + // 数据连接名称 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Name'), + forms: [ + { + type: TextChecker.xtype, + $value: 'connection-name', + width: EDITOR_WIDTH, + value: connectionName, + allowBlank: true, + ref: (_ref: TextChecker) => { + this.form.connectionName = _ref; + }, + watermark: BI.i18nText('Dec-Dcm_Data_Connections'), + }, + ], + }, + // 版本 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Basic_Version'), + invisible: BI.isUndefined(databaseType.versions), + forms: [ + { + type: BI.TextValueCombo.xtype, + width: EDITOR_WIDTH, + value: this.version, + items: () => databaseType.versions.map(item => { + return { + text: BI.i18nText('Dec-Migration_Database_Version', item), + value: item, + } + }), + ref: (_ref: TextValueCombo) => { + this.form.version = _ref; + }, + listeners: [ + { + eventName: BI.TextValueCombo.EVENT_CHANGE, + action: () => { + const version = this.form.version.getValue()[0]; + this.version = version; + this.sslCollapse.setCollapse(true); + this.sslCollapse.setVisible(this.getSslSetEnabled()); + !BI.isUndefined(databaseType.hasSchemas) && this.schemaForm.setVisible(databaseType.hasSchemas[version]); + !BI.isUndefined(databaseType.versionConfig?.[version]?.fetchSize) && this.form.fetchSize.setValue(databaseType.versionConfig[version].fetchSize); + this.form.driver.setDefaultDrivers(version); + }, + }, + ], + }, + ], + }, + // 驱动 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Driver'), + forms: [ + { + type: DriverSelector.xtype, + ref: (_ref: DriverSelector) => { + this.form.driver = _ref; + }, + driver, + driverSource, + connectionData, + version: this.version, + listeners: [ + { + eventName: 'EVENT_CHANGE', + action: () => { + const value = this.form.driver.getValue(); + const connectionData = this.options.formData.connectionData as ConnectionJDBC; + const connectionType = getJdbcDatabaseType(connectionData.database, connectionData.driver); + // DEC-2020 + const url = (connectionType.urls && connectionType.urls[value.driver]) || connectionType.url; + this.form.url.setValue(url); + const urlInfo = resolveUrlInfo(url, connectionData.database); + this.form.host.setValue(urlInfo.host); + this.form.database.setValue(urlInfo.databaseName); + this.form.port.setValue(urlInfo.port); + }, + }, + ], + }, + ], + }, + // catalog + { + type: FormItem.xtype, + name: 'catalog', + invisible: database !== 'starrocks', + forms: [ + { + type: BI.TextEditor.xtype, + $value: 'database-catalog', + width: EDITOR_WIDTH, + allowBlank: true, + watermark: 'catalog', + value: catalog, + ref: (_ref: any) => { + this.form.catalog = _ref; + }, + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.onHostPortChange(databaseType); + }, + }, + ], + }, + ], + }, + // 数据库名称 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), + forms: [ + { + type: BI.TextEditor.xtype, + $value: 'database-name', + width: EDITOR_WIDTH, + allowBlank: true, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Name'), + value: databaseName, + ref: (_ref: any) => { + this.form.database = _ref; + }, + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.onHostPortChange(databaseType); + }, + }, + ], + }, + ], + }, + // 主机 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + forms: [ + { + type: BI.TextEditor.xtype, + $value: 'database-host', + width: EDITOR_WIDTH, + allowBlank: true, + value: host, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + ref: (_ref: any) => { + this.form.host = _ref; + }, + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.onHostPortChange(databaseType); + }, + }, + ], + }, + ], + }, + // 端口 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + forms: [ + { + type: TextChecker.xtype, + $value: 'database-port', + width: EDITOR_WIDTH, + allowBlank: true, + value: port, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.port = _ref; + }, + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.onHostPortChange(databaseType); + }, + }, + ], + }, + ], + }, + // 认证方式 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_AuthType'), + invisible: !databaseType.kerberos, + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'auth-type', + width: EDITOR_WIDTH, + value: authType, + ref: (_ref: TextValueCombo) => { + this.form.authType = _ref; + }, + items: [ + { + text: BI.i18nText('Dec-Dcm_Connection_Form_UserName_Password'), + value: '', + }, + { + text: 'Kerberos', + value: 'kerberos', + }, + ], + listeners: [ + { + eventName: BI.Combo.EVENT_CHANGE, + action: () => { + const type = this.form.authType.getValue()[0]; + this.formPrincipal.setVisible(!!type); + this.formKeyPath.setVisible(!!type); + this.formKrb5File.setVisible(!!type); + this.form.krb5Path.setEnable(!type); + this.formUser.setVisible(!type); + this.formPassword.setVisible(!type); + }, + }, + ], + }, + ], + }, + // 用户名 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + invisible: !!authType, + ref: (_ref: FormItem) => { + this.formUser = _ref; + }, + forms: [ + { + type: BI.TextEditor.xtype, + $value: 'username', + width: EDITOR_WIDTH, + allowBlank: true, + value: user, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + ref: (_ref: TextEditor) => { + this.form.user = _ref; + }, + }, + ], + }, + // 密码 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Password'), + invisible: !!authType, + ref: (_ref: FormItem) => { + this.formPassword = _ref; + }, + forms: [ + { + type: BI.Editor.xtype, + $value: 'password', + cls: 'bi-border-bottom', + width: EDITOR_WIDTH, + height: EDITOR_HEIGHT, + allowBlank: true, + value: password, + inputType: 'password', + autocomplete: 'new-password', + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Password'), + ref: (_ref: Editor) => { + this.form.password = _ref; + }, + }, + ], + }, + // keytab密钥路径 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_KeyPath'), + invisible: !authType, + ref: (_ref: FormItem) => { + this.formKeyPath = _ref; + }, + forms: [ + { + type: FileUpload.xtype, + $value: 'key-path', + processId: "keytab", + allowBlank: true, + value: keyPath, + accept: '.keytab', + inter: '/v10/config/connection/upload/keytab?', + iconCls: "data-keytab-file", + watermark: BI.i18nText('Dec-Dcm_Connection_Form_KeyPath'), + ref: (_ref: FileUpload) => { + this.form.keyPath = _ref; + }, + listeners: [ + { + eventName: FileUpload.EVENT_CHECK_SUCCESS, + action: (value) => { + const principalsItems = BI.map(value.principals, function (index, item) { + return { + text: item, + value: item + } + }) + this.form.principal.populate(principalsItems); + this.form.principal.setValue(principalsItems[0].value); + this.setKerberos(); + this.form.krb5Path.setEnable(true); + }, + }, { + eventName: FileUpload.EVENT_CLEAR_FILE, + action: () => { + this.form.krb5Path.setEnable(false); + this.form.principal.populate(); + this.form.principal.setValue(); + } + } + ] + }, + ], + }, + // 客户端principal + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Principal'), + invisible: !authType, + ref: (_ref: FormItem) => { + this.formPrincipal = _ref; + }, + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'principal', + width: EDITOR_WIDTH, + value: principal, + ref: (_ref: TextEditor) => { + this.form.principal = _ref; + }, + listeners: [ + { + eventName: BI.Combo.EVENT_CHANGE, + action: () => { + this.changePrincipal(); + this.setKerberos(); + }, + }, + ], + } + ], + }, + // krb5.conf文件 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Krb5File'), + invisible: !authType, + ref: (_ref: FormItem) => { + this.formKrb5File = _ref; + }, + forms: [ + { + type: FileUpload.xtype, + $value: 'krb5-file', + processId: "krb5Conf", + allowBlank: true, + value: krb5Path, + iconCls: "data-conf-file", + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Krb5File'), + ref: (_ref: FileUpload) => { + this.form.krb5Path = _ref; + }, + inter: '/v10/config/connection/upload/krb5?', + accept: '.conf', + listeners: [ + { + eventName: FileUpload.EVENT_CHECK_SUCCESS, + action: () => { + this.setKerberos(); + }, + } + ] + } + ] + }, + // 编码 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_OriginalCharsetName'), + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'original-charset-name', + width: EDITOR_WIDTH, + value: originalCharsetName ? originalCharsetName : '', + items: CONNECT_CHARSET, + ref: (_ref: TextValueCombo) => { + this.form.originalCharsetName = _ref; + }, + }, + ], + }, + // 模式 + { + type: FormItem.xtype, + invisible: BI.isUndefined(databaseType.hasSchemas) ? !databaseType.hasSchema : !databaseType.hasSchemas[this.version], + name: BI.i18nText('Dec-Dcm_Connection_Form_Pattern'), + forms: [ + { + type: BI.VerticalLayout.xtype, + height: 55, + items: [ + { + type: BI.FloatLeftLayout.xtype, + items: [ + { + type: BI.TextButton.xtype, + cls: 'bi-high-light', + text: BI.i18nText('Dec-Dcm_Connection_Click_Connect_Database'), + handler: () => { + this.fireEvent('EVENT_TEST_CONNECTION'); + }, + }, + { + type: BI.Label.xtype, + cls: 'bi-tips', + lgap: 3, + text: BI.i18nText('Dec-Dcm_Connection_Read_Mode_List'), + }, + ], + }, + { + type: BI.TextValueCombo.xtype, + _tgap: 15, + $value: 'schema', + width: EDITOR_WIDTH, + disabled: true, + value: schema, + items: schema ? [{ text: schema, value: schema }] : [], + ref: (_ref: TextValueCombo) => { + this.form.schema = _ref; + }, + }, + ], + }, + ], + ref: (_ref: FormItem) => { + this.schemaForm = _ref; + }, + }, + // 并行装载设置 + { + type: Collapse.xtype, + invisible: BI.isNull(parallelLoad), + name: BI.i18nText('Dec-Dcm_Connection_Setting', BI.i18nText('Dec-Dcm_Connection_Parallel_Load')), + el: { + type: TipsCombo.xtype, + _lgap: 6, + el: { + type: BI.CenterAdaptLayout.xtype, + innerHgap: 15, + innerVgap: 10, + items: [ + { + type: BI.Label.xtype, + text: BI.i18nText('Dec-Dcm_Connection_Setting_Tips', BI.i18nText('Dec-Dcm_Connection_Parallel_Load')), + }, + /** + * FIXME: 帮助链接待提供 + */ + { + type: BI.A.xtype, + href: '', + el: { + type: BI.Label.xtype, + text: BI.i18nText('Dec-BI_Help_Paper'), + } + } + ] + } + }, + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.parallelLoadSet.setVisible(!isCollapse); + } + } + ] + }, + { + type: BI.VerticalLayout.xtype, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.parallelLoadSet = _ref; + }, + items: [ + { + // 服务器地址-节点1 + type: FormItem.xtype, + name: `${BI.i18nText('Dec-Dcm_Connection_Server_Address')}-${BI.i18nText('Dec-Memory_Detection_Server_Cluster_Node', '1')}`, + _bgap: vgap, + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'server-cluster-node', + width: EDITOR_WIDTH, + value: parallelLoad?.serverAddress ?? '', + items: parallelLoad?.serverAddressItems || [], + listeners: [ + { + eventName: BI.TextValueCombo.EVENT_CHANGE, + action: (value: string) => { + this.form.parallelLoad.serverAddress = value; + } + } + ] + }, + ], + }, + { + // 复用临时表 + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Reuse_Temporary_Table'), + _bgap: vgap, + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'reuse-temporary-table', + width: EDITOR_WIDTH, + value: parallelLoad?.reuseTemporaryTable ?? 0, + items: YES_OR_NO, + watermark: BI.i18nText('Dec-Dcm_Connection_Reuse_Temporary_Table'), + listeners: [ + { + eventName: BI.TextValueCombo.EVENT_CHANGE, + action: (value: number) => { + this.form.parallelLoad.isReuseTemporaryTable = value; + } + } + ] + }, + ], + }, + { + // 临时文件条数限制 + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Pieces_Limit'), + _bgap: vgap, + forms: [ + { + type: BI.Editor.xtype, + $value: 'temporary-file-pieces-limit', + cls: 'bi-border-bottom', + width: EDITOR_WIDTH, + height: EDITOR_HEIGHT, + allowBlank: true, + value: parallelLoad?.filePiecesLimit ?? '', + watermark: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Pieces_Limit'), + ref: (_ref: Editor) => { + this.form.parallelLoad.filePiecesLimit = _ref; + }, + }, + ], + }, + { + // 临时文件大小限制(MB) + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Size_Limit'), + forms: [ + { + type: BI.Editor.xtype, + $value: 'temporary-file-size-limit', + cls: 'bi-border-bottom', + width: EDITOR_WIDTH, + height: EDITOR_HEIGHT, + allowBlank: true, + value: parallelLoad?.fileSizeLimit ?? '', + watermark: BI.i18nText('Dec-Dcm_Connection_Temporary_File_Size_Limit'), + ref: (_ref: Editor) => { + this.form.parallelLoad.fileSizeLimit = _ref; + }, + }, + ], + }, + ] + }, + // HDFS设置 + { + type: Collapse.xtype, + invisible: BI.isNull(hdfs), + name: BI.i18nText('Dec-Dcm_Connection_Setting', 'HDFS'), + el: { + type: TipsCombo.xtype, + _lgap: 6, + el: { + type: BI.CenterAdaptLayout.xtype, + innerHgap: 15, + innerVgap: 10, + items: [ + { + type: BI.Label.xtype, + text: BI.i18nText('Dec-Dcm_Connection_Setting_Tips', 'HDFS'), + }, + { + type: BI.A.xtype, + href: '', + el: { + type: BI.Label.xtype, + text: BI.i18nText('Dec-BI_Help_Paper'), + } + } + ] + } + }, + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.hdfsSet.setVisible(!isCollapse); + } + } + ] + }, + { + type: BI.VerticalLayout.xtype, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.hdfsSet = _ref; + }, + items: [ + { + // HDFS地址 + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Address', 'HDFS'), + forms: [ + { + type: BI.Editor.xtype, + $value: 'hdfs-connection-address', + cls: 'bi-border-bottom', + width: EDITOR_WIDTH, + height: EDITOR_HEIGHT, + allowBlank: true, + value: hdfs?.hdfsAddress ?? '', + watermark: BI.i18nText('Dec-Dcm_Connection_Address', 'HDFS'), + ref: (_ref: Editor) => { + this.form.hdfs.hdfsAddress = _ref; + }, + }, + ], + } + ] + }, + // 分隔线 + { + type: BI.Layout.xtype, + cls: 'bi-border-top', + bgap: 8, + }, + // 数据连接URL + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), + forms: [ + { + type: BI.TextEditor.xtype, + $value: 'database-url', + width: EDITOR_WIDTH, + allowBlank: true, + value: url, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_URL'), + ref: (_ref: TextEditor) => { + this.form.url = _ref; + }, + listeners: [ + { + eventName: 'EVENT_CHANGE', + action: () => { + const urlInfo = resolveUrlInfo(this.form.url.getValue(), database); + this.form.host.setValue(urlInfo.host); + this.form.catalog.setValue(urlInfo.catalog); + this.form.database.setValue(urlInfo.databaseName); + this.form.port.setValue(urlInfo.port); + }, + }, + ], + }, + ], + }, + // 最大活动连接数 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), + forms: [ + { + type: TextChecker.xtype, + $value: 'max-active', + width: EDITOR_WIDTH, + allowBlank: false, + value: maxActive, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Active'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.maxActive = _ref; + }, + }, + ], + }, + // 获取连接前检验 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'check', + width: EDITOR_WIDTH, + allowBlank: true, + value: testOnBorrow, + items: this.getBooleanItem(), + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Borrow'), + ref: (_ref: TextValueCombo) => { + this.form.testOnBorrow = _ref; + }, + }, + ], + }, + // 空闲连接可用性定期检查 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow'), + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'keep-alive-check', + width: EDITOR_WIDTH, + allowBlank: true, + value: keepAlive, + items: this.getBooleanItem(), + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow'), + ref: (_ref: TextValueCombo) => { + this.form.keepAlive = _ref; + }, + }, + ], + }, + // 校验语句 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_SQL_Validation_Query'), + forms: [ + { + type: BI.TextAreaEditor.xtype, + $value: 'validation-query', + cls: 'bi-border', + allowBlank: true, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_SQL_Validation_Query_Watermark'), + value: api.getPlain(validationQuery || ''), + width: EDITOR_WIDTH, + height: 100, + ref: (_ref: TextAreaEditor) => { + this.form.validationQuery = _ref; + }, + }, + ], + }, + // 最大等待时间 + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), + forms: [ + { + type: TextChecker.xtype, + $value: 'max-wait', + width: EDITOR_WIDTH, + allowBlank: false, + value: maxWait, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Wait'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.maxWait = _ref; + }, + }, + { + type: BI.Label.xtype, + lgap: 5, + height: CONNECTION_LAYOUT.labelHeight, + text: BI.i18nText('Dec-Dcm_Millisecond'), + }, + ], + }, + // SSH设置 + { + type: Collapse.xtype, + width: 100, + name: BI.i18nText('Dec-Dcm_Connection_Setting', 'SSH'), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.sshSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + ref: (_ref: VerticalLayout) => { + this.sshSet = _ref; + }, + bgap: vgap, + invisible: true, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Tunnel', 'SSH'), + forms: [ + { + type: BI.MultiSelectItem.xtype, + ref: (_ref: MultiSelectItem) => { + this.form.usingSsh = _ref; + }, + logic: { dynamic: true }, + text: BI.i18nText('Dec-Basic_Use') + BI.i18nText('Dec-Dcm_Connection_Tunnel', 'SSH'), + selected: usingSsh, + listeners: [ + { + eventName: BI.MultiSelectItem.EVENT_CHANGE, + action: () => { + const value = this.form.usingSsh.isSelected(); + this.sshForm.setVisible(value); + }, + }, + ], + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + ref: (_ref: VerticalLayout) => { + this.sshForm = _ref; + }, + bgap: vgap, + invisible: !usingSsh, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + forms: [ + { + type: TextChecker.xtype, + ref: (_ref: TextChecker) => { + this.form.sshIp = _ref; + }, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Host'), + value: sshIp, + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.form.sshSecret.setValue(""); + } + } + ] + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + forms: [ + { + type: TextChecker.xtype, + ref: (_ref: TextChecker) => { + this.form.sshPort = _ref; + }, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Port'), + allowBlank: false, + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + value: String(sshPort || 22), + listeners: [ + { + eventName: BI.Editor.EVENT_CHANGE, + action: () => { + this.form.sshSecret.setValue(""); + } + } + ] + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + forms: [ + { + type: TextChecker.xtype, + ref: (_ref: TextChecker) => { + this.form.sshUser = _ref; + }, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_UserName'), + value: sshUser, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_VerifyType'), + forms: [ + { + type: BI.TextValueCombo.xtype, + ref: (_ref: TextValueCombo) => { + this.form.sshType = _ref; + }, + width: EDITOR_WIDTH, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_VerifyType'), + items: CONNECT_SSH_TYPE, + value: sshType, + listeners: [ + { + eventName: BI.TextValueCombo.EVENT_CHANGE, + action: () => { + const sshType = this.form.sshType.getValue()[0]; + this.onSshTypeChange(sshType); + }, + }, + ], + }, + ], + }, + { + type: FormItem.xtype, + ref: (_ref: FormItem) => { + this.form.sshPrivateKeyPathForm = _ref; + }, + name: BI.i18nText('Dec-Dcm_Connection_Form_PrivateKey'), + forms: [ + { + type: FileChooser.xtype, + ref: (_ref: TextChecker) => { + this.form.sshPrivateKeyPath = _ref; + }, + root: 'certificates', + watermark: BI.i18nText('Dec-Dcm_Connection_Form_PrivateKey'), + value: sshPrivateKeyPath, + }, + ], + }, + { + type: FormItem.xtype, + ref: (ref: FormItem) => { + this.form.sshSecretForm = ref; + }, + name: BI.i18nText(''), + forms: [ + { + type: TextChecker.xtype, + ref: (_ref: TextChecker) => { + this.form.sshSecret = _ref; + }, + watermark: BI.i18nText(''), + inputType: 'password', + autocomplete: 'new-password', + value: sshSecret, + }, + ], + }, + ], + }, + }, + ], + }, + }, + // SSL设置 + { + type: Collapse.xtype, + width: 100, + name: BI.i18nText('Dec-Dcm_Connection_Setting', 'SSL'), + invisible: !this.getSslSetEnabled(), + ref: (_ref: Collapse) => { + this.sslCollapse = _ref; + }, + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.sslSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + ref: (_ref: VerticalLayout) => { + this.sslSet = _ref; + }, + bgap: vgap, + invisible: true, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Tunnel', 'SSL'), + forms: [ + { + type: BI.MultiSelectItem.xtype, + ref: (_ref: MultiSelectItem) => { + this.form.usingSsl = _ref; + }, + logic: { dynamic: true }, + text: BI.i18nText('Dec-Basic_Use') + BI.i18nText('Dec-Dcm_Connection_Tunnel', 'SSL'), + selected: usingSsl, + listeners: [ + { + eventName: BI.MultiSelectItem.EVENT_CHANGE, + action: () => { + const value = this.form.usingSsl.isSelected(); + this.sslForm.setVisible(value); + }, + }, + ], + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + ref: (_ref: VerticalLayout) => { + this.sslForm = _ref; + }, + bgap: vgap, + invisible: !usingSsl, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_CA_Certificate'), + forms: [ + { + type: FileChooser.xtype, + ref: (_ref: FileChooser) => { + this.form.caCertificate = _ref; + }, + root: 'certificates', + watermark: BI.i18nText('Dec-Dcm_Connection_Form_CA_Certificate'), + value: caCertificate, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Verify_CA_Certificate'), + forms: [ + { + type: BI.TextValueCombo.xtype, + ref: (_ref: TextValueCombo) => { + this.form.verifyCa = _ref; + }, + width: EDITOR_WIDTH, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Verify_CA_Certificate'), + items: this.getBooleanItem(), + value: verifyCa, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_SecretKey'), + forms: [ + { + type: FileChooser.xtype, + ref: (_ref: FileChooser) => { + this.form.sslClientPrivateKey = _ref; + }, + root: 'certificates', + watermark: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_SecretKey'), + value: sslClientPrivateKey, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_Certificate'), + forms: [ + { + type: FileChooser.xtype, + ref: (_ref: FileChooser) => { + this.form.sslClientCertificate = _ref; + }, + root: 'certificates', + watermark: BI.i18nText('Dec-Dcm_Connection_Client') + BI.i18nText('Dec-Dcm_Connection_Form_Certificate'), + value: sslClientCertificate, + }, + ], + }, + ], + }, + }, + ], + }, + }, + // 高级设置 + { + type: Collapse.xtype, + width: 100, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_More_Setting'), + listeners: [ + { + eventName: EVENT_CHANGE, + action: (isCollapse: boolean) => { + this.advancedSet.setVisible(!isCollapse); + }, + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + vgap: vgap, + top: -15, + invisible: true, + ref: (_ref: VerticalLayout) => { + this.advancedSet = _ref; + }, + items: [ + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), + forms: [ + { + type: TextChecker.xtype, + $value: 'initial-size', + width: EDITOR_WIDTH, + allowBlank: false, + value: initialSize, + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Initial_Size'), + ref: (_ref: TextChecker) => { + this.form.initialSize = _ref; + }, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), + forms: [ + { + type: TextChecker.xtype, + $value: 'min-idle', + width: EDITOR_WIDTH, + allowBlank: false, + value: minIdle, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Idle'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.minIdle = _ref; + }, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'test-on-return', + width: EDITOR_WIDTH, + allowBlank: true, + value: testOnReturn, + items: this.getBooleanItem(), + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_On_Return'), + ref: (_ref: TextValueCombo) => { + this.form.testOnReturn = _ref; + }, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), + forms: [ + { + type: BI.TextValueCombo.xtype, + $value: 'test-while-idle', + width: EDITOR_WIDTH, + allowBlank: true, + value: testWhileIdle, + items: this.getBooleanItem(), + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_While_Idle'), + ref: (_ref: TextValueCombo) => { + this.form.testWhileIdle = _ref; + }, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), + forms: [ + { + type: TextChecker.xtype, + $value: 'test-between-evicition-millis', + width: EDITOR_WIDTH, + allowBlank: false, + value: timeBetweenEvictionRunsMillis, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Number'), + checker: (value: string) => this.checkNumber(value), + autoFix: true, + }, + { + errorText: BI.i18nText('Dec-Util-Must_Less_Than', BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis'), BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow_Threshold')), + checker: (value: string) => this.checkNumber(value) && (parseInt(value) < parseInt(this.form.keepAliveBetweenTimeMillis.getValue())), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.timeBetweenEvictionRunsMillis = _ref; + }, + }, + { + type: BI.Label.xtype, + lgap: 5, + height: CONNECTION_LAYOUT.labelHeight, + text: BI.i18nText('Dec-Dcm_Millisecond'), + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), + forms: [ + { + type: TextChecker.xtype, + $value: 'test-pereviction-run-num', + width: EDITOR_WIDTH, + allowBlank: false, + value: numTestsPerEvictionRun, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Tests_PerEviction_Run_Num'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.numTestsPerEvictionRun = _ref; + }, + }, + ], + }, + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), + forms: [ + { + type: TextChecker.xtype, + $value: 'min-evictable-idle-time-millis', + width: EDITOR_WIDTH, + allowBlank: false, + value: minEvictableIdleTimeMillis, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 30), + checker: (value: string) => this.checkNumber(value) && (parseInt(value) >= 30), + autoFix: true, + }, + { + errorText: BI.i18nText('Dec-Util-Must_Less_Than', BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis'), BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Evictable_Idle_Time_Millis')), + checker: (value: string) => parseInt(value) < parseInt(this.form.maxEvictableIdleTimeMillis.getValue()), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.minEvictableIdleTimeMillis = _ref; + }, + }, + { + type: BI.Label.xtype, + lgap: 5, + height: CONNECTION_LAYOUT.labelHeight, + text: BI.i18nText('BI-Basic_Seconds'), + }, + ], + }, + + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Evictable_Idle_Time_Millis'), + forms: [ + { + type: TextChecker.xtype, + $value: 'max-evictable-idle-time-millis', + width: EDITOR_WIDTH, + allowBlank: false, + value: maxEvictableIdleTimeMillis, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Evictable_Idle_Time_Millis'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 0), + checker: (value: string) => this.checkInteger(value), + autoFix: true, + }, + { + errorText: BI.i18nText('Dec-Util-Must_More_Than', BI.i18nText('Dec-Dcm_Connection_Form_Database_Max_Evictable_Idle_Time_Millis'), BI.i18nText('Dec-Dcm_Connection_Form_Database_Min_Evictable_Idle_Time_Millis')), + checker: (value: string) => parseInt(value) > parseInt(this.form.minEvictableIdleTimeMillis.getValue()), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.maxEvictableIdleTimeMillis = _ref; + }, + }, + { + type: BI.Label.xtype, + lgap: 5, + height: CONNECTION_LAYOUT.labelHeight, + text: BI.i18nText('BI-Basic_Seconds'), + }, + ], + }, + + { + type: FormItem.xtype, + name: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow_Threshold'), + forms: [ + { + type: TextChecker.xtype, + $value: 'keep-live-between-time-millis', + width: EDITOR_WIDTH, + allowBlank: false, + value: keepAliveBetweenTimeMillis, + watermark: BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow_Threshold'), + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Integer', 30000), + checker: (value: string) => this.checkNumber(value) && (parseInt(value) > 30000), + autoFix: true, + }, + { + errorText: BI.i18nText('Dec-Util-Must_More_Than', BI.i18nText('Dec-Dcm_Connection_Form_Database_Regular_Check_On_Borrow_Threshold'), BI.i18nText('Dec-Dcm_Connection_Form_Database_Test_Between_Eviction_Millis')), + checker: (value: string) => this.checkNumber(value) && (parseInt(value) > parseInt(this.form.timeBetweenEvictionRunsMillis.getValue())), + autoFix: true, + }, + valueRangeConfig, + ], + ref: (_ref: TextChecker) => { + this.form.keepAliveBetweenTimeMillis = _ref; + }, + }, + { + type: BI.Label.xtype, + lgap: 5, + height: CONNECTION_LAYOUT.labelHeight, + text: BI.i18nText('Dec-Dcm_Millisecond'), + }, + ], + }, + { + el: { + type: BI.VerticalLayout.xtype, + cls: 'bi-border-top', + items: [ + { + el: { + type: FormItem.xtype, + name: 'Fetchsize', + forms: [ + { + type: TextChecker.xtype, + $value: 'fetch-size', + width: EDITOR_WIDTH, + allowBlank: true, + value: fetchSize, + watermark: 'Fetchsize', + validationChecker: [ + { + errorText: BI.i18nText('Dec-Dcm_Connection_Check_Fetch_Size_Range'), + checker: (value: string) => + BI.isInteger(value) && BI.parseInt(value) >= 0 && BI.parseInt(value) <= 1000000, + autoFix: true, + }, + ], + ref: (_ref: TextChecker) => { + this.form.fetchSize = _ref; + }, + }, + ], + }, + vgap: 15, + }, + ], + }, + }, + ], + }, + }, + ], + }; + } + + public mounted() { + const sshType = this.form.sshType.getValue()[0]; + this.onSshTypeChange(sshType); + this.setKerberos(); + } + + public setError(value: string) { + this.form.connectionName.setError(value); + } + + private checkInteger(value: string) { + return /^[\d]+$/.test(value); + } + + private checkNumber(value: string) { + return /^[(\-|\+)?\d]+$/.test(value); + } + + private checkValueRange(value: string) { + return parseInt(value, 0) <= INT_MAX_VALUE && parseInt(value, 0) >= INT_MIN_VALUE; + } + + private getDrivers() { + const connectionData = this.options.formData.connectionData as ConnectionJDBC; + const connectionType = getJdbcDatabaseType(connectionData.database, connectionData.driver); + const drivers = connectionType.drivers + ? connectionType.drivers.map((item) => { + return { + text: item, + value: item, + }; + }) + : [ + { + text: connectionType.driver, + value: connectionType.driver, + }, + ]; + + if (!drivers.some((item) => item.text === connectionData.driver)) { + return [ + { + text: connectionData.driver, + value: connectionData.driver, + }, + ...drivers, + ]; + } + + return drivers; + } + + private getBooleanItem() { + return [ + { + text: BI.i18nText('Dec-Dcm_Yes'), + value: true, + }, + { + text: BI.i18nText('Dec-Dcm_No'), + value: false, + }, + ]; + } + + private onHostPortChange(database) { + const { urls, url, databaseType } = database; + const driver = this.form.driver.getValue(); + const selectUrl = BI.get(urls, driver.driver) || url; + const host = this.form.host.getValue(); + const port = this.form.port.getValue(); + const catalog = this.form.catalog.getValue(); + const databaseName = this.form.database.getValue(); + this.form.url.setValue(splitUrl(host, port, catalog, databaseName, selectUrl, databaseType)); + } + + private onSshTypeChange(sshType) { + const { privateKeyPathFormVisible, secretFormName } = CONNECT_SSH_TYPE.find((SSH_TYPE) => sshType === SSH_TYPE.value); + this.form.sshPrivateKeyPathForm.setVisible(privateKeyPathFormVisible); + this.form.sshSecretForm.setName(secretFormName); + this.form.sshSecret.setWatermark(secretFormName); + } + + private getSslSetEnabled(): boolean { + const { databaseType } = this.databaseType; + return databaseType === 'mysql' || this.version === 'mysql'; + } + + public setSchemas(schemas: string[]) { + this.form.schema.setEnable(true); + if (schemas.length > 0) { + const value = this.form.schema.getValue()[0]; + this.form.schema.populate( + schemas.map((item) => { + return { + text: item, + value: item, + }; + }) + ); + this.form.schema.setValue(value && schemas.some((item) => item === value) ? value : schemas[0]); + } + } + + public setKerberos() { + const KerberosParams = BI.extend({}, { + keytabPath: this.form.keyPath.getValue(), + krb5ConfPath: this.form.krb5Path.getValue(), + principal: this.form.principal.getValue()[0] + }); + this.form.krb5Path.setFileInfo(KerberosParams); + this.form.keyPath.setFileInfo(KerberosParams); + } + + public initPrincipals(keyPath, principal) { + let self = this; + api.getPrincipals(keyPath).then(res => { + const principalsItems = BI.map(res.data, function (index, item) { + return { + text: item, + value: item + } + }); + self.form.principal.populate(principalsItems); + self.form.principal.setValue(principal); + }) + } + + public changePrincipal() { + let self = this; + const KerberosParams = BI.extend({}, { + keytabPath: this.form.keyPath.getValue(), + krb5ConfPath: this.form.krb5Path.getValue(), + principal: this.form.principal.getValue()[0] + }); + api.changePrincipal(KerberosParams).then(res => { + self.form.keyPath.setValue(res.data.keytabPath); + self.form.krb5Path.setValue(res.data.krb5ConfPath); + }); + + } + + public validation(): boolean { + const driver = this.form.driver.validation(); + const sshSet = !this.form.usingSsh.isSelected() || BI.isNotEmptyString(this.form.sshPort.getValue()); + + return driver && sshSet; + } + + public getSubmitValue(): Connection { + const connectionData = this.options.formData.connectionData as ConnectionJDBC; + const connectionPoolAttr = connectionData.connectionPoolAttr; + const originalCharsetName = this.form.originalCharsetName.getValue()[0] || ''; + // TODO 获取表单数据这里待优化 + const { parallelLoad, hdfs } = this.form; + + return { + connectionType: connectionType.JDBC, + connectionId: this.form.connectionName.getValue(), + connectionName: this.form.connectionName.getValue(), + connectionData: BI.extend({}, connectionData, { + database: connectionData.database, + connectionName: this.form.connectionName.getValue(), + ...this.form.driver.getValue(), + url: this.form.url.getValue(), + user: this.form.user.getValue(), + password: this.oldPassword === this.form.password.getValue() ? this.oldPassword : api.getCipher(this.form.password.getValue()), + queryType: '', + newCharsetName: originalCharsetName ? 'gbk' : '', // 后台要求,originalCharsetName不为空时,newCharsetName为gbk + originalCharsetName, + schema: this.form.schema.getValue()[0], + host: this.form.host.getValue(), + authType: this.form.authType.getValue()[0] || '', + creator: Dec ? Dec.personal.username : '', + principal: this.form.principal.getValue()[0], + keyPath: this.form.keyPath.getValue(), + krb5Path: this.form.krb5Path.getValue(), + fetchSize: BI.isEmptyString(this.form.fetchSize.getValue()) ? -1 : BI.parseInt(this.form.fetchSize.getValue()), + // ssh + usingSsh: this.form.usingSsh.isSelected(), + // redirectPort: 0, + // redirectIp: '', + // sshKeepAlive: 10000, + // sshTimeOut: 10000, + sshIp: this.form.sshIp.getValue(), + sshPort: Number(this.form.sshPort.getValue()), + sshUser: this.form.sshUser.getValue(), + sshType: this.form.sshType.getValue()[0], + sshPrivateKeyPath: this.form.sshPrivateKeyPath.getValue(), + sshSecret: this.oldSshSecret === this.form.sshSecret.getValue() ? this.oldSshSecret : api.getCipher(this.form.sshSecret.getValue()), + // ssl + usingSsl: this.getSslSetEnabled() && this.form.usingSsl.isSelected(), + caCertificate: this.form.caCertificate.getValue(), + verifyCa: this.form.verifyCa.getValue()[0], + sslType: CONNECT_SSL_TYPE[0].value, + sslClientPrivateKey: this.form.sslClientPrivateKey.getValue(), + sslClientCertificate: this.form.sslClientCertificate.getValue(), + // 连接池 + connectionPoolAttr: { + initialSize: this.form.initialSize.getValue(), + maxActive: this.form.maxActive.getValue(), + minIdle: this.form.minIdle.getValue(), + maxWait: this.form.maxWait.getValue(), + validationQuery: api.getCipher(this.form.validationQuery.getValue()), + testOnBorrow: BI.size(this.form.testOnBorrow.getValue()) > 0 ? this.form.testOnBorrow.getValue()[0] : connectionPoolAttr.testOnBorrow, + keepAlive: BI.size(this.form.keepAlive.getValue()) > 0 ? this.form.keepAlive.getValue()[0] : connectionPoolAttr.keepAlive, + testOnReturn: BI.size(this.form.testOnReturn.getValue()) > 0 ? this.form.testOnReturn.getValue()[0] : connectionPoolAttr.testOnReturn, + testWhileIdle: BI.size(this.form.testWhileIdle.getValue()) > 0 ? this.form.testWhileIdle.getValue()[0] : connectionPoolAttr.testWhileIdle, + timeBetweenEvictionRunsMillis: this.form.timeBetweenEvictionRunsMillis.getValue(), + numTestsPerEvictionRun: this.form.numTestsPerEvictionRun.getValue(), + minEvictableIdleTimeMillis: this.form.minEvictableIdleTimeMillis.getValue(), + maxEvictableIdleTimeMillis: this.form.maxEvictableIdleTimeMillis.getValue(), + keepAliveBetweenTimeMillis: this.form.keepAliveBetweenTimeMillis.getValue(), + }, + // 并行装载 + parallelLoad: { + serverAddress: parallelLoad.serverAddress, + isReuseTemporaryTable: parallelLoad.isReuseTemporaryTable, + filePiecesLimit: parallelLoad.filePiecesLimit.getValue(), + fileSizeLimit: parallelLoad.fileSizeLimit.getValue(), + }, + hdfs: { + hdfsAddress: hdfs.hdfsAddress.getValue(), + } + // HDFS + }), + }; + } +} diff --git a/src/modules/pages/maintain/forms/components/form.plugin.ts b/src/modules/pages/maintain/forms/components/form.plugin.ts index 6282203..881089a 100644 --- a/src/modules/pages/maintain/forms/components/form.plugin.ts +++ b/src/modules/pages/maintain/forms/components/form.plugin.ts @@ -37,4 +37,8 @@ export class FormPlugin extends BI.Widget { connectionData: BI.extend({}, connectionData, this.plugin.getValue()), }; } + + public getSaveFn() { + return this.plugin.save; + } } diff --git a/src/modules/pages/maintain/forms/form.model.ts b/src/modules/pages/maintain/forms/form.model.ts index 3bbc66e..7947b15 100644 --- a/src/modules/pages/maintain/forms/form.model.ts +++ b/src/modules/pages/maintain/forms/form.model.ts @@ -17,6 +17,7 @@ export class MaintainFormModel extends Model<{ pageIndex: AppModel['TYPE']['pageIndex']; connections: AppModel['TYPE']['connections']; isCopy: AppModel['TYPE']['isCopy']; + connectionSelected: AppModel['TYPE']['connectionSelected']; }, context: MaintainFormModel['context']; }> { @@ -31,6 +32,7 @@ export class MaintainFormModel extends Model<{ 'testEvent', 'connections', 'isCopy', + 'connectionSelected', ]; actions = { @@ -51,5 +53,8 @@ export class MaintainFormModel extends Model<{ goFirstPage() { this.model.pageIndex = PAGE_INDEX.CONNECTION; }, + setConnectionSelected(name: string) { + this.model.connectionSelected = name; + } } } diff --git a/src/modules/pages/maintain/forms/form.server.ts b/src/modules/pages/maintain/forms/form.server.ts index d378768..2098ac1 100644 --- a/src/modules/pages/maintain/forms/form.server.ts +++ b/src/modules/pages/maintain/forms/form.server.ts @@ -5,6 +5,7 @@ import { TestStatus } from '../../../components/test_status/test_status'; import { getJdbcDatabaseType } from '../../../app.service'; import { ApiFactory } from '../../../crud/apiFactory'; const api = new ApiFactory().create(); + export function testConnection(value: Connection): Promise { return new Promise(resolve => { let testStatus = null; @@ -15,12 +16,18 @@ export function testConnection(value: Connection): Promise { return false; } + const id = BI.UUID(); const testConnection = () => { const formValue = value; + api.testConnection(formValue).then(re => { if (re && re.errorCode) { - // 判断是否是缺少驱动,如果缺少驱动则显示下载驱动的连接 + if (re.errorCode === DecCst.ErrorCode.NO_IP_AUTHORIZED) { + testStatus.setFail(); + return; + } + // 判断是否是缺少驱动,如果缺少驱动则显示下载驱动的连接 if (api.isDriverError(re.errorCode)) { if (formValue.connectionType === connectionType.JDBC) { const driver = (formValue.connectionData as ConnectionJDBC).driver; @@ -40,7 +47,11 @@ export function testConnection(value: Connection): Promise { } else if (re.errorCode === errorCode.DUPLICATE_NAMES) { testStatus.setFail(BI.i18nText(re.errorMsg)); } else { + // 不缺少驱动,但连接失败,打印出当前驱动加载路径,并显示检测驱动按钮 testStatus.setFail(re.errorMsg); + api.getDriverLoadPath(formValue).then(res => { + testStatus.setExtraContainer(createDriverTestContainer(res.data)); + }) } } else if (re.data) { testStatus.setSuccess(); @@ -55,7 +66,54 @@ export function testConnection(value: Connection): Promise { BI.Maskers.remove(id); } }); + + /** + * 驱动及冲突检测内容,补充到报错弹窗里 + */ + function createDriverTestContainer(path: string) { + return { + type: BI.VerticalLayout.xtype, + vgap: 5, + items: [ + { + type: BI.Label.xtype, + text: BI.i18nText('Dec-Connection_Driver_Current_Load_Path', path), + textAlign: 'left', + whiteSpace: 'normal', + }, + { + type: BI.TextButton.xtype, + cls: 'bi-high-light', + text: BI.i18nText('Dec-Connection_Driver_Check'), + textAlign: 'left', + handler: () => { + api.checkDriverStatus({ + driver: (formValue.connectionData as ConnectionJDBC).driver, + path, + }).then(res => { + const isDriverConflict = res.data; + + testStatus.setExtraContainer({ + type: BI.VerticalLayout.xtype, + items: [ + { + type: BI.Label.xtype, + textAlign: 'left', + text: isDriverConflict + ? BI.i18nText('Dec-Connection_Driver_Has_Confilt_Tip') + : BI.i18nText('Dec-Connection_Driver_No_Confilt_Tip'), + cls: isDriverConflict ? 'bi-error' : '', + } + ] + }) + }); + } + } + ] + } + } }; + BI.Maskers.create(id, null, { render: { type: TestStatus.xtype, diff --git a/src/modules/pages/maintain/forms/form.ts b/src/modules/pages/maintain/forms/form.ts index cb3b573..2e6248b 100644 --- a/src/modules/pages/maintain/forms/form.ts +++ b/src/modules/pages/maintain/forms/form.ts @@ -6,13 +6,15 @@ import { FormPlugin } from './components/form.plugin'; import { connectionType, errorCode } from '@constants/env'; import { ConnectionJDBC, Connection, ResultType } from 'src/modules/crud/crud.typings'; import { DEFAULT_JNDI_DATA, DEFAULT_JDBC_POOL, DATEBASE_FILTER_TYPE } from '@constants/constant'; -import { getJdbcDatabaseType, getChartLength } from '../../../app.service'; +import { getJdbcDatabaseType, getChartLength, getAllDatabaseTypes } from '../../../app.service'; import { NAME_MAX_LENGTH } from '../../../app.constant'; +import { checkIllegalStrings } from "@core/index"; @shortcut() @store(MaintainFormModel) export class MaintainForm extends BI.Widget { static xtype = 'dec.dcm.maintain.form'; + static EVENT_TESTCONNECTION = 'EVENT_TEST_CONNECTION'; props = { connectionType: '', @@ -28,29 +30,37 @@ export class MaintainForm extends BI.Widget { watch = { saveEvent: () => { - if (!this.testValue()) { - return; - } - if (this.connectionName && !this.model.isCopy) { - const value = this.form.getSubmitValue(); - (value.connectionData as ConnectionJDBC).creator = BI.get(this.getFormData(), 'creator'); - // DEC-10155 为了适配插件的数据连接,在外层也加一个creator字段 - value.creator = BI.get(this.getFormData(), 'creator'); - this.store.updateConnection(this.connectionName, value).then(result => { - if (result.errorCode) { - this.showError(result); + const sonSave = this.form.getSaveFn?.(); + if (sonSave) { + sonSave().then((success: boolean) => { + if (success) { + this.store.goFirstPage(); + BI.Msg.toast(BI.i18nText("Dec-Basic_Save_Success"), { + level: "success", + }); + } else { + BI.Msg.toast(BI.i18nText("Dec-Basic_Save_Fail"), { + level: "error", + }); + } + }); + + //只有外来插件才可以使用是否不执行平台的保存逻辑 + if (this.model.datebaseTypeSelected) { + const isPluginDatabase = BI.some(BI.Providers.getProvider('dec.connection.provider.datebase').customDatabaseType, (_index, value) => value.databaseType === this.model.datebaseTypeSelected); + if (isPluginDatabase && this.model.datebaseTypeSelectedOne.isNoSave) { return; } - this.store.goFirstPage(); - }); - } else { - const form = this.form.getSubmitValue(); - form.connectionId = this.connectionName; - // DEC-10155 为了适配插件的数据连接,在外层也加一个creator字段 - form.creator = Dec ? Dec.personal.username : ''; - this.addConnection(form); + } else { + const databaseType = this.model.connectionSelectedOne.connectionType; + const database = BI.find(getAllDatabaseTypes(), (_index, value) => value.databaseType === databaseType); + if (this.model.connectionSelectedOne.pluginConnection && database?.isNoSave) { + return; + } + } } + this.save(); }, testEvent: () => { this.testConnection(); @@ -71,7 +81,7 @@ export class MaintainForm extends BI.Widget { this.form = _ref; }, listeners: [{ - eventName: 'EVENT_TEST_CONNECTION', + eventName: MaintainForm.EVENT_TESTCONNECTION, action: () => { this.testConnection(); }, @@ -116,15 +126,16 @@ export class MaintainForm extends BI.Widget { let editConnection: Connection; let connectionData: ConnectionJDBC; if (this.model.datebaseTypeSelected) { + const { databaseType: database, fetchSize } = this.model.datebaseTypeSelectedOne; + connectionData = { - driver: this.model.datebaseTypeSelectedOne.driver, - url: this.model.datebaseTypeSelectedOne.url, - database: this.model.datebaseTypeSelectedOne.databaseType, + ...this.model.datebaseTypeSelectedOne, + database, connectionName, connectionPoolAttr: DEFAULT_JDBC_POOL, port: '', host: 'localhost', - fetchSize: this.model.datebaseTypeSelectedOne.fetchSize ?? -1, + fetchSize: fetchSize ?? -1, identity: BI.UUID(), }; editConnection = { @@ -139,9 +150,12 @@ export class MaintainForm extends BI.Widget { this.isEdit = true; this.connectionName = this.model.connectionSelectedOne.connectionName; const connection = BI.clone(this.model.connectionSelectedOne); - const { database, driver } = connection.connectionData as ConnectionJDBC; - (connection.connectionData as ConnectionJDBC).database = getJdbcDatabaseType(database, driver).databaseType; - + connectionData = connection.connectionData as ConnectionJDBC; + const { database, driver } = connectionData; + connectionData.database = getJdbcDatabaseType(database, driver).databaseType; + if (this.model.isCopy) { + connectionData.identity = BI.UUID(); + } return connection; } @@ -200,6 +214,12 @@ export class MaintainForm extends BI.Widget { if (this.form.validation && !this.form.validation()) { return false; } + const result = checkIllegalStrings(value.connectionName); + if (!result.legal) { + this.setFromError(result.errorMsg); + + return false; + } return true; } @@ -250,6 +270,11 @@ export class MaintainForm extends BI.Widget { private addConnection(form: Connection) { this.store.addConnection(form).then(result => { if (result.errorCode) { + if (result.errorCode === DecCst.ErrorCode.NO_IP_AUTHORIZED) { + BI.Msg.toast(BI.i18nText("Dec-Basic_Save_Fail"), { + level: "error", + }); + } if (result.errorCode === errorCode.DUPLICATE_NAMES) { if (form.connectionType !== connectionType.JDBC && form.connectionType !== connectionType.JNDI) { // 如果不是jdbc或jndi,即如果是插件,名称重复的时候需要修改名字重新提交给后台 @@ -270,8 +295,41 @@ export class MaintainForm extends BI.Widget { return; } + // 新增之后connections待更新,connectionSelected先置空 + this.store.setConnectionSelected(''); this.store.goFirstPage(); this.store.setIsCopy(false); }); } + + private save() { + if (!this.testValue()) { + return; + } + if (this.connectionName && !this.model.isCopy) { + const value = this.form.getSubmitValue(); + (value.connectionData as ConnectionJDBC).creator = BI.get(this.getFormData(), 'creator'); + // DEC-10155 为了适配插件的数据连接,在外层也加一个creator字段 + value.creator = BI.get(this.getFormData(), 'creator'); + this.store.updateConnection(this.connectionName, value).then(result => { + if (result.errorCode) { + if (result.errorCode === DecCst.ErrorCode.NO_IP_AUTHORIZED) { + BI.Msg.toast(BI.i18nText("Dec-Basic_Save_Fail"), { + level: "error", + }); + } + this.showError(result); + + return; + } + this.store.goFirstPage(); + }); + } else { + const form = this.form.getSubmitValue(); + form.connectionId = this.connectionName; + // DEC-10155 为了适配插件的数据连接,在外层也加一个creator字段 + form.creator = Dec ? Dec.personal.username : ''; + this.addConnection(form); + } + } } diff --git a/src/modules/pages/maintain/maintain.ts b/src/modules/pages/maintain/maintain.ts index 32ed22a..4511666 100644 --- a/src/modules/pages/maintain/maintain.ts +++ b/src/modules/pages/maintain/maintain.ts @@ -9,7 +9,7 @@ import './maintain.less'; import { connectionType } from '@constants/env'; import { getJdbcDatabaseType, getTextByDatabaseType } from '../../app.service'; import { ConnectionJDBC } from 'src/modules/crud/crud.typings'; -import { ListView } from '@fui/core'; +import { ButtonGroup } from '@fui/core'; @shortcut() @store(MaintainModel) @@ -19,7 +19,7 @@ export class Maintain extends BI.Widget { model: MaintainModel['model']; store: MaintainModel['store']; - listView: ListView; + buttonGroup: ButtonGroup; socketTip: LinkButton; render() { @@ -28,7 +28,7 @@ export class Maintain extends BI.Widget { return { type: BI.VTapeLayout.xtype, - hgap: 5, + hgap: 16, items: [ { type: BI.VerticalAdaptLayout.xtype, @@ -64,9 +64,9 @@ export class Maintain extends BI.Widget { ], }, { - type: BI.ListView.xtype, - ref: (_ref: ListView) => { - this.listView = _ref; + type: BI.ButtonGroup.xtype, + ref: (_ref: ButtonGroup) => { + this.buttonGroup = _ref; }, items: this.renderItems(), }, diff --git a/src/modules/pages/setting/setting.ts b/src/modules/pages/setting/setting.ts new file mode 100644 index 0000000..d788cb5 --- /dev/null +++ b/src/modules/pages/setting/setting.ts @@ -0,0 +1,96 @@ +import { shortcut } from '@core/core'; +import { ApiFactory } from 'src/modules/crud/apiFactory'; + +const api = new ApiFactory().create(); + +@shortcut() +export class TimeOutSetting extends BI.Widget { + public static xtype = 'dec.dcm.page.timeout.setting'; + + public props = { + value: 0, + }; + + beforeRender(cb: Function) { + const self = this; + api.getTimeOut().then(res => { + self.props.value = res.data.count; + cb(); + }); + } + + public render() { + const { value } = this.props; + const self = this; + + return { + type: 'bi.vtape', + cls: 'bi-background', + items: [{ + type: 'dec.setting.header', + height: 40, + listeners: [{ + eventName: 'EVENT_CANCEL', + action: function () { + self.fireEvent('EVENT_CHANGE'); + }, + }, { + eventName: 'EVENT_SAVE', + action: function () { + api.putTimeOut(Number(self.editor.getValue())); + self.fireEvent('EVENT_CHANGE'); + }, + }], + }, { + type: 'bi.vertical', + cls: 'bi-card', + vgap: 10, + items: [ + { + el: { + type: 'bi.vertical_adapt', + cls: 'bi-border-bottom', + height: 40, + items: [{ + type: 'bi.label', + textAlign: 'left', + width: 120, + cls: 'dec-font-weight-bold', + text: BI.i18nText('Dec-Dcm_Connection_Timeout_Detection'), + }] + }, tgap: -10, hgap: 16, + }, + { + type: 'bi.vertical_adapt', + hgap: 16, + items: [{ + type: 'dec.label.editor.item', + text: BI.i18nText('Dec-Over_Time'), + textWidth: 100, + editorWidth: 80, + allowBlank: false, + value: value, + validationChecker: function(v) { + return BI.isPositiveInteger(v); + }, + errorText: BI.i18nText('BI-Please_Input_Positive_Integer'), + ref: function (_ref) { + self.editor = _ref; + }, + }, { + el: { + type: 'bi.label', + text: BI.i18nText('Dec-Dcm_Connection_Timeout_Millisecond'), + }, + lgap: 10, + }] + + }, + ] + }], + ref: function (_ref) { + self.setting = _ref; + }, + }; + } +} diff --git a/src/modules/title/title.ts b/src/modules/title/title.ts index dc85b0f..28858fb 100644 --- a/src/modules/title/title.ts +++ b/src/modules/title/title.ts @@ -11,7 +11,7 @@ export class Title extends BI.Widget { static xtype = 'dec.dcm.title'; props = { - baseCls: 'bi-card', + baseCls: '', } tab: Tab; linearSegment: LinearSegment; @@ -31,6 +31,14 @@ export class Title extends BI.Widget { type: BI.LeftRightVerticalAdaptLayout.xtype, items: { left: [ + { + type: 'bi.icon_button', + cls: 'setting-font', + _lgap: 15, + handler: () => { + this.fireEvent('EVENT_CLICK_SETTING'); + }, + }, { type: BI.LinearSegment.xtype, cls: 'bi-font-bold', @@ -70,6 +78,7 @@ export class Title extends BI.Widget { type: BI.Tab.xtype, height: 40, showIndex: this.model.pageIndex, + width:200, ref: (_ref: Tab) => { this.tab = _ref; }, diff --git a/src/modules/title/title_database/title_datebase.ts b/src/modules/title/title_database/title_datebase.ts index 1441d75..6541237 100644 --- a/src/modules/title/title_database/title_datebase.ts +++ b/src/modules/title/title_database/title_datebase.ts @@ -25,13 +25,13 @@ export class TitleDatabase extends BI.Widget { items: [{ type: BI.VerticalAdaptLayout.xtype, height: 40, - rgap: 5, + rgap: 16, items: [ { type: BI.Button.xtype, $value: 'title-database-cancel', text: BI.i18nText('BI-Basic_Cancel'), - level: 'ignore', + light: true, handler: () => { this.store.setPageIndex(PAGE_INDEX.CONNECTION); }, @@ -49,6 +49,7 @@ export class TitleDatabase extends BI.Widget { }, }, ], + _rgap: -16, }], }; } diff --git a/src/modules/title/title_maintain/title_maintain.model.ts b/src/modules/title/title_maintain/title_maintain.model.ts index 21fd87a..c3bc416 100644 --- a/src/modules/title/title_maintain/title_maintain.model.ts +++ b/src/modules/title/title_maintain/title_maintain.model.ts @@ -11,12 +11,13 @@ export class TitleMaintainModel extends Model<{ connectionSelected: AppModel['TYPE']['connectionSelected']; testEvent: AppModel['TYPE']['testEvent']; isCopy: AppModel['TYPE']['isCopy']; + noTestConnection: AppModel['TYPE']['noTestConnection']; }, context: TitleMaintainModel['context']; }> { static xtype = 'dec.dcm.model.title_maintain'; - context = ['pageIndex', 'saveEvent', 'testEvent', 'connectionSelected', 'isCopy']; + context = ['pageIndex', 'saveEvent', 'testEvent', 'connectionSelected', 'isCopy', 'noTestConnection']; actions = { setPageIndex: (index: string) => { diff --git a/src/modules/title/title_maintain/title_maintain.ts b/src/modules/title/title_maintain/title_maintain.ts index 18fdc2c..2f3c48f 100644 --- a/src/modules/title/title_maintain/title_maintain.ts +++ b/src/modules/title/title_maintain/title_maintain.ts @@ -1,6 +1,7 @@ import { shortcut, store } from '@core/core'; import { TitleMaintainModel } from './title_maintain.model'; import { PAGE_INDEX } from '@constants/constant'; +import { Button } from '@fui/core'; @shortcut() @store(TitleMaintainModel) @@ -9,19 +10,20 @@ export class TitleMaintain extends BI.Widget { store: TitleMaintainModel['store']; model: TitleMaintainModel['model']; + render() { return { type: BI.FloatRightLayout.xtype, items: [{ type: BI.VerticalAdaptLayout.xtype, height: 40, - rgap: 5, items: [ { type: BI.Button.xtype, $value: 'title-maintain-cancel', text: BI.i18nText('BI-Basic_Cancel'), - level: 'ignore', + clear: true, + _rgap: 16, handler: () => { this.store.setIsCopy(false); this.store.setPageIndex(PAGE_INDEX.CONNECTION); @@ -30,15 +32,21 @@ export class TitleMaintain extends BI.Widget { }, { type: BI.Button.xtype, + ref: (_ref) => { + this.testConnectionBtn = _ref; + }, + invisible: () => this.model.noTestConnection, + _rgap: 16, $value: 'title-maintain-connection-test', text: BI.i18nText('Dec-Dcm_Connection_Test'), - level: 'ignore', + light: true, handler: () => { this.store.setTestEvent(); }, }, { type: BI.Button.xtype, + _rgap: 16, $value: 'title-maintain-save', text: BI.i18nText('BI-Basic_Save'), handler: () => { diff --git a/tsconfig.json b/tsconfig.json index 4e9e3db..bac5abb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,8 @@ "module": "es2015", "moduleResolution": "node", "lib": [ - "es2017", - "dom" + "es2017", + "dom" ], "declaration": true, "experimentalDecorators": true, @@ -19,13 +19,23 @@ // "noUnusedParameters": true, // "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, + "skipLibCheck": false, "paths": { - "ui": ["./src/ui"], - "ReportCst": ["./private/constants"], - "types": ["./types/index.d.ts"], - "@core/*": ["./src/modules/core/*"], - "@constants/*": ["./src/modules/constants/*"] + "ui": [ + "./src/ui" + ], + "ReportCst": [ + "./private/constants" + ], + "types": [ + "./types/index.d.ts" + ], + "@core/*": [ + "./src/modules/core/*" + ], + "@constants/*": [ + "./src/modules/constants/*" + ] } }, "include": [ @@ -33,6 +43,6 @@ "src/**/*.ts", "private/*.ts", "private/**/*.ts", - "types/globals.d.ts" + "types" ] } \ No newline at end of file diff --git a/types/globals.d.ts b/types/globals.d.ts index 09ede39..a00fd3b 100644 --- a/types/globals.d.ts +++ b/types/globals.d.ts @@ -2,6 +2,8 @@ interface Obj { [key: string]: any; } +type RequestFunction = (url: string, data: any, callback: (re: any) => void) => void; + declare let BI: Obj & import('@fui/core').BI & import('@fui/materials').BI; declare const Fix: Obj; declare const DecCst: Obj; @@ -14,15 +16,21 @@ declare const Dec: { hasListeners: Function; removeListener: Function; }; - system: { - - }; + system: {}; personal: { username: string; }; + Utils: Obj; + reqByEncrypt: (method: AxiosType.X_Method, url: string, data?: any, config?: AxiosType.X_AxiosRequestConfig) => {}, socketEmit: (type: string, name: string, callback: (re: any) => void) => void; - reqGet: (url: string, data: any, callback: (re: any) => void) => void; - reqPost: (url: string, data: any, callback: (re: any) => void) => void; - reqPut: (url: string, data: any, callback: (re: any) => void) => void; - reqDelete: (url: string, data: any, callback: (re: any) => void) => void; -}; \ No newline at end of file + // req + reqGet: RequestFunction; + reqPost: RequestFunction; + reqPut: RequestFunction; + reqDelete: RequestFunction; + // reqHandle + reqGetHandle: RequestFunction; + reqPostHandle: RequestFunction; + reqPutHandle: RequestFunction; + reqDeleteHandle: RequestFunction; +}; diff --git a/types/request.d.ts b/types/request.d.ts new file mode 100644 index 0000000..4042317 --- /dev/null +++ b/types/request.d.ts @@ -0,0 +1,9 @@ +import { Method, AxiosRequestConfig } from 'axios'; + +declare namespace AxiosType { + type X_Method = Method + interface X_AxiosRequestConfig extends AxiosRequestConfig { } +} + +export = AxiosType; +export as namespace AxiosType; \ No newline at end of file