From 7a24be06724094c17381effc0baeae1dd018f2e6 Mon Sep 17 00:00:00 2001 From: IT-Kwj <49737232+IT-Kwj@users.noreply.github.com> Date: Wed, 28 Jun 2023 10:56:41 +0800 Subject: [PATCH] [Feature-14404][datasource-plugin] add HANA datasource (#14404) --------- Co-authored-by: WeiJian Kong <> Co-authored-by: xujiaqiang --- docs/configs/docsdev.js | 8 + docs/docs/en/guide/datasource/hana.md | 22 +++ docs/docs/zh/guide/datasource/hana.md | 17 ++ docs/img/new_ui/dev/datasource/hana.png | Bin 0 -> 104421 bytes .../common/constants/DataSourceConstants.java | 5 + .../hana/HanaDataSourceChannel.java | 31 ++++ .../hana/HanaDataSourceChannelFactory.java | 37 +++++ .../datasource/hana/HanaDataSourceClient.java | 30 ++++ .../hana/param/HanaConnectionParam.java | 38 +++++ .../hana/param/HanaDataSourceParamDTO.java | 43 +++++ .../hana/param/HanaDataSourceProcessor.java | 130 ++++++++++++++++ .../HanaDataSourceChannelFactoryTest.java | 32 ++++ .../hana/HanaDataSourceChannelTest.java | 38 +++++ .../param/HanaDataSourceProcessorTest.java | 105 +++++++++++++ .../provider/JDBCDataSourceProviderTest.java | 62 ++++++++ .../hana/utils/DataSourceUtilsTest.java | 147 ++++++++++++++++++ .../dolphinscheduler/spi/enums/DbType.java | 3 +- .../src/service/modules/data-source/types.ts | 1 + .../src/views/datasource/list/use-form.ts | 5 + .../components/node/fields/use-datasource.ts | 5 + 20 files changed, 758 insertions(+), 1 deletion(-) create mode 100644 docs/docs/en/guide/datasource/hana.md create mode 100644 docs/docs/zh/guide/datasource/hana.md create mode 100644 docs/img/new_ui/dev/datasource/hana.png create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannel.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactory.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceClient.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaConnectionParam.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceParamDTO.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessor.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactoryTest.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelTest.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessorTest.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/provider/JDBCDataSourceProviderTest.java create mode 100644 dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/utils/DataSourceUtilsTest.java diff --git a/docs/configs/docsdev.js b/docs/configs/docsdev.js index 6c63f157df..49c05f87f5 100644 --- a/docs/configs/docsdev.js +++ b/docs/configs/docsdev.js @@ -347,6 +347,10 @@ export default { title: 'Databend', link: '/en-us/docs/dev/user_doc/guide/datasource/databend.html', }, + { + title: 'HANA', + link: '/en-us/docs/dev/user_doc/guide/datasource/hana.html', + } ], }, { @@ -1044,6 +1048,10 @@ export default { title: 'Databend', link: '/zh-cn/docs/dev/user_doc/guide/datasource/databend.html', }, + { + title: 'HANA', + link: '/zh-cn/docs/dev/user_doc/guide/datasource/hana.html', + } ], }, { diff --git a/docs/docs/en/guide/datasource/hana.md b/docs/docs/en/guide/datasource/hana.md new file mode 100644 index 0000000000..28912801f9 --- /dev/null +++ b/docs/docs/en/guide/datasource/hana.md @@ -0,0 +1,22 @@ +# HANA + +![hana](../../../../img/new_ui/dev/datasource/hana.png) + +## Datasource Parameters + +| **Datasource** | **Description** | +|----------------------------|---------------------------------------------------------| +| Datasource | Select HANA. | +| Datasource name | Enter the name of the DataSource. | +| Description | Enter a description of the DataSource. | +| IP/Host Name | Enter the HANA service IP. | +| Port | Enter the HANA service port. | +| Username | Set the username for HANA connection. | +| Password | Set the password for HANA connection. | +| Database name | Enter the database name of the HANA connection. | +| Jdbc connection parameters | Parameter settings for HANA connection, in JSON format. | + +## Native Supported + +No, read section example in [datasource-setting](../howto/datasource-setting.md) `DataSource Center` section to activate this datasource. + diff --git a/docs/docs/zh/guide/datasource/hana.md b/docs/docs/zh/guide/datasource/hana.md new file mode 100644 index 0000000000..12c17b6ffd --- /dev/null +++ b/docs/docs/zh/guide/datasource/hana.md @@ -0,0 +1,17 @@ +# HANA 数据源 + +![hana](../../../../img/new_ui/dev/datasource/hana.png) + +- 数据源:选择 HANA +- 数据源名称:输入数据源的名称 +- 描述:输入数据源的描述 +- IP 主机名:输入连接 HANA 的 IP +- 端口:输入连接 HANA 的端口 +- 用户名:设置连接 HANA 的用户名 +- 密码:设置连接 HANA 的密码 +- 数据库名:输入连接 HANA 的数据库名称 +- Jdbc 连接参数:用于 HANA 连接的参数设置,以 JSON 形式填写 + +## 是否原生支持 + +否,使用前需请参考 [数据源配置](../howto/datasource-setting.md) 中的 "数据源中心" 章节激活数据源。 diff --git a/docs/img/new_ui/dev/datasource/hana.png b/docs/img/new_ui/dev/datasource/hana.png new file mode 100644 index 0000000000000000000000000000000000000000..eb030587377b663fd288a0a2dce3b1d2e33da43b GIT binary patch literal 104421 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 H??3%Njm4Ad literal 0 HcmV?d00001 diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java index 9e7a378307..4c1c3e30ac 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/DataSourceConstants.java @@ -42,6 +42,8 @@ public class DataSourceConstants { public static final String COM_OCEANBASE_JDBC_DRIVER = "com.oceanbase.jdbc.Driver"; public static final String NET_SNOWFLAKE_JDBC_DRIVER = "net.snowflake.client.jdbc.SnowflakeDriver"; public static final String COM_VERTICA_JDBC_DRIVER = "com.vertica.jdbc.Driver"; + public static final String COM_HANA_DB_JDBC_DRIVER = "com.sap.db.jdbc.Driver"; + /** * validation Query */ @@ -63,6 +65,8 @@ public class DataSourceConstants { public static final String KYUUBI_VALIDATION_QUERY = "select 1"; public static final String VERTICA_VALIDATION_QUERY = "select 1"; + public static final String HANA_VALIDATION_QUERY = "select 1"; + /** * jdbc url */ @@ -85,6 +89,7 @@ public class DataSourceConstants { public static final String JDBC_OCEANBASE = "jdbc:oceanbase://"; public static final String JDBC_SNOWFLAKE = "jdbc:snowflake://"; public static final String JDBC_VERTICA = "jdbc:vertica://"; + public static final String JDBC_HANA = "jdbc:sap://"; /** * database type diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannel.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannel.java new file mode 100644 index 0000000000..9952ee4316 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannel.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana; + +import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; +import org.apache.dolphinscheduler.spi.datasource.DataSourceChannel; +import org.apache.dolphinscheduler.spi.datasource.DataSourceClient; +import org.apache.dolphinscheduler.spi.enums.DbType; + +public class HanaDataSourceChannel implements DataSourceChannel { + + @Override + public DataSourceClient createDataSourceClient(BaseConnectionParam baseConnectionParam, DbType dbType) { + return new HanaDataSourceClient(baseConnectionParam, dbType); + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactory.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactory.java new file mode 100644 index 0000000000..75aacebaff --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactory.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana; + +import org.apache.dolphinscheduler.spi.datasource.DataSourceChannel; +import org.apache.dolphinscheduler.spi.datasource.DataSourceChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(DataSourceChannelFactory.class) +public class HanaDataSourceChannelFactory implements DataSourceChannelFactory { + + @Override + public String getName() { + return "hana"; + } + + @Override + public DataSourceChannel create() { + return new HanaDataSourceChannel(); + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceClient.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceClient.java new file mode 100644 index 0000000000..72806e42df --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceClient.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana; + +import org.apache.dolphinscheduler.plugin.datasource.api.client.CommonDataSourceClient; +import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; +import org.apache.dolphinscheduler.spi.enums.DbType; + +public class HanaDataSourceClient extends CommonDataSourceClient { + + public HanaDataSourceClient(BaseConnectionParam baseConnectionParam, DbType dbType) { + super(baseConnectionParam, dbType); + } + +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaConnectionParam.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaConnectionParam.java new file mode 100644 index 0000000000..6236e71cc8 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaConnectionParam.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.param; + +import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; + +public class HanaConnectionParam extends BaseConnectionParam { + + @Override + public String toString() { + return "HanaConnectionParam{" + + "user='" + user + '\'' + + ", password='" + password + '\'' + + ", address='" + address + '\'' + + ", database='" + database + '\'' + + ", jdbcUrl='" + jdbcUrl + '\'' + + ", driverLocation='" + driverLocation + '\'' + + ", driverClassName='" + driverClassName + '\'' + + ", validationQuery='" + validationQuery + '\'' + + ", other='" + other + '\'' + + '}'; + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceParamDTO.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceParamDTO.java new file mode 100644 index 0000000000..294450250f --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceParamDTO.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.param; + +import org.apache.dolphinscheduler.plugin.datasource.api.datasource.BaseDataSourceParamDTO; +import org.apache.dolphinscheduler.spi.enums.DbType; + +public class HanaDataSourceParamDTO extends BaseDataSourceParamDTO { + + @Override + public String toString() { + return "HanaDataSourceParamDTO{" + + "name='" + name + '\'' + + ", note='" + note + '\'' + + ", host='" + host + '\'' + + ", port=" + port + + ", database='" + database + '\'' + + ", userName='" + userName + '\'' + + ", password='" + password + '\'' + + ", other='" + other + '\'' + + '}'; + } + + @Override + public DbType getType() { + return DbType.HANA; + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessor.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessor.java new file mode 100644 index 0000000000..5c5da73aab --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessor.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.param; + +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.constants.DataSourceConstants; +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.datasource.api.datasource.AbstractDataSourceProcessor; +import org.apache.dolphinscheduler.plugin.datasource.api.datasource.BaseDataSourceParamDTO; +import org.apache.dolphinscheduler.plugin.datasource.api.datasource.DataSourceProcessor; +import org.apache.dolphinscheduler.plugin.datasource.api.utils.PasswordUtils; +import org.apache.dolphinscheduler.spi.datasource.BaseConnectionParam; +import org.apache.dolphinscheduler.spi.datasource.ConnectionParam; +import org.apache.dolphinscheduler.spi.enums.DbType; + +import org.apache.commons.collections4.MapUtils; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.auto.service.AutoService; +@AutoService(DataSourceProcessor.class) +public class HanaDataSourceProcessor extends AbstractDataSourceProcessor { + + private final Logger logger = LoggerFactory.getLogger(HanaDataSourceProcessor.class); + + private static final String APPEND_PARAMS = "reconnect=true"; + @Override + public BaseDataSourceParamDTO castDatasourceParamDTO(String paramJson) { + return JSONUtils.parseObject(paramJson, HanaDataSourceParamDTO.class); + } + + @Override + public BaseDataSourceParamDTO createDatasourceParamDTO(String connectionJson) { + HanaConnectionParam connectionParams = (HanaConnectionParam) createConnectionParams(connectionJson); + HanaDataSourceParamDTO mysqlDatasourceParamDTO = new HanaDataSourceParamDTO(); + mysqlDatasourceParamDTO.setUserName(connectionParams.getUser()); + mysqlDatasourceParamDTO.setDatabase(connectionParams.getDatabase()); + String address = connectionParams.getAddress(); + String[] hostSeperator = address.split(Constants.DOUBLE_SLASH); + String[] hostPortArray = hostSeperator[hostSeperator.length - 1].split(Constants.COMMA); + mysqlDatasourceParamDTO.setPort(Integer.parseInt(hostPortArray[0].split(Constants.COLON)[1])); + mysqlDatasourceParamDTO.setHost(hostPortArray[0].split(Constants.COLON)[0]); + + return mysqlDatasourceParamDTO; + } + + @Override + public BaseConnectionParam createConnectionParams(BaseDataSourceParamDTO dataSourceParam) { + HanaDataSourceParamDTO hanaDatasourceParam = (HanaDataSourceParamDTO) dataSourceParam; + String address = String.format("%s%s:%s", DataSourceConstants.JDBC_HANA, hanaDatasourceParam.getHost(), + hanaDatasourceParam.getPort()); + String jdbcUrl = String.format("%s?currentschema=%s", address, hanaDatasourceParam.getDatabase()); + + HanaConnectionParam hanaConnectionParam = new HanaConnectionParam(); + hanaConnectionParam.setJdbcUrl(jdbcUrl); + hanaConnectionParam.setValidationQuery("select 1 from DUMMY"); + hanaConnectionParam.setDatabase(hanaDatasourceParam.getDatabase()); + hanaConnectionParam.setAddress(address); + hanaConnectionParam.setUser(hanaDatasourceParam.getUserName()); + hanaConnectionParam.setPassword(PasswordUtils.encodePassword(hanaDatasourceParam.getPassword())); + hanaConnectionParam.setDriverClassName(getDatasourceDriver()); + hanaConnectionParam.setOther(hanaDatasourceParam.getOther()); + + return hanaConnectionParam; + } + + @Override + public ConnectionParam createConnectionParams(String connectionJson) { + return JSONUtils.parseObject(connectionJson, HanaConnectionParam.class); + } + + @Override + public String getDatasourceDriver() { + return DataSourceConstants.COM_HANA_DB_JDBC_DRIVER; + } + + @Override + public String getValidationQuery() { + return DataSourceConstants.COM_HANA_DB_JDBC_DRIVER; + } + + @Override + public String getJdbcUrl(ConnectionParam connectionParam) { + HanaConnectionParam hanaConnectionParam = (HanaConnectionParam) connectionParam; + String jdbcUrl = hanaConnectionParam.getJdbcUrl(); + if (MapUtils.isNotEmpty(hanaConnectionParam.getOther())) { + return String.format("%s?%s&%s", jdbcUrl, hanaConnectionParam.getOther(), APPEND_PARAMS); + } + return String.format("%s&%s", jdbcUrl, APPEND_PARAMS); + } + + @Override + public Connection getConnection(ConnectionParam connectionParam) throws ClassNotFoundException, SQLException { + HanaConnectionParam mysqlConnectionParam = (HanaConnectionParam) connectionParam; + Class.forName(getDatasourceDriver()); + String user = mysqlConnectionParam.getUser(); + String password = PasswordUtils.decodePassword(mysqlConnectionParam.getPassword()); + return DriverManager.getConnection(getJdbcUrl(connectionParam), user, password); + } + + @Override + public DbType getDbType() { + return DbType.HANA; + } + + @Override + public DataSourceProcessor create() { + return new HanaDataSourceProcessor(); + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactoryTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactoryTest.java new file mode 100644 index 0000000000..089b203f50 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelFactoryTest.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana; + +import org.apache.dolphinscheduler.spi.datasource.DataSourceChannel; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class HanaDataSourceChannelFactoryTest { + + @Test + public void testCreate() { + HanaDataSourceChannelFactory sourceChannelFactory = new HanaDataSourceChannelFactory(); + DataSourceChannel dataSourceChannel = sourceChannelFactory.create(); + Assertions.assertNotNull(dataSourceChannel); + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelTest.java new file mode 100644 index 0000000000..e60e3bfc81 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/HanaDataSourceChannelTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana; + +import org.apache.dolphinscheduler.plugin.datasource.hana.param.HanaConnectionParam; +import org.apache.dolphinscheduler.spi.enums.DbType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class HanaDataSourceChannelTest { + + @Test + public void testCreateDataSourceClient() { + HanaDataSourceChannel sourceChannel = Mockito.mock(HanaDataSourceChannel.class); + HanaDataSourceClient dataSourceClient = Mockito.mock(HanaDataSourceClient.class); + Mockito.when(sourceChannel.createDataSourceClient(Mockito.any(), Mockito.any())).thenReturn(dataSourceClient); + Assertions.assertNotNull(sourceChannel.createDataSourceClient(new HanaConnectionParam(), DbType.HANA)); + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessorTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessorTest.java new file mode 100644 index 0000000000..8f6095b3b5 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/param/HanaDataSourceProcessorTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.param; + +import org.apache.dolphinscheduler.common.constants.DataSourceConstants; +import org.apache.dolphinscheduler.plugin.datasource.api.utils.PasswordUtils; +import org.apache.dolphinscheduler.spi.enums.DbType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +@ExtendWith(MockitoExtension.class) +public class HanaDataSourceProcessorTest { + + private HanaDataSourceProcessor hanaDataSourceProcessor = new HanaDataSourceProcessor(); + + @Test + public void testCreateConnectionParams() { + Map props = new HashMap<>(); + HanaDataSourceParamDTO mysqlDatasourceParamDTO = new HanaDataSourceParamDTO(); + mysqlDatasourceParamDTO.setUserName("root"); + mysqlDatasourceParamDTO.setPassword("123456"); + mysqlDatasourceParamDTO.setHost("localhost"); + mysqlDatasourceParamDTO.setPort(30015); + mysqlDatasourceParamDTO.setDatabase("default"); + mysqlDatasourceParamDTO.setOther(props); + try (MockedStatic mockedPasswordUtils = Mockito.mockStatic(PasswordUtils.class)) { + Mockito.when(PasswordUtils.encodePassword(Mockito.anyString())).thenReturn("test"); + HanaConnectionParam connectionParams = (HanaConnectionParam) hanaDataSourceProcessor + .createConnectionParams(mysqlDatasourceParamDTO); + Assertions.assertEquals("jdbc:sap://localhost:30015", connectionParams.getAddress()); + Assertions.assertEquals("jdbc:sap://localhost:30015?currentschema=default", connectionParams.getJdbcUrl()); + } + } + + @Test + public void testCreateConnectionParams2() { + String connectionJson = "{\"user\":\"root\",\"password\":\"123456\",\"address\":\"jdbc:sap://localhost:30015\"" + + ",\"database\":\"default\",\"jdbcUrl\":\"jdbc:sap://localhost:30015?currentschema=default\"}"; + HanaConnectionParam connectionParams = (HanaConnectionParam) hanaDataSourceProcessor + .createConnectionParams(connectionJson); + Assertions.assertNotNull(connectionJson); + Assertions.assertEquals("root", connectionParams.getUser()); + } + + @Test + public void testGetDatasourceDriver() { + Assertions.assertEquals(DataSourceConstants.COM_HANA_DB_JDBC_DRIVER, + hanaDataSourceProcessor.getDatasourceDriver()); + } + + @Test + public void testGetJdbcUrl() { + HanaConnectionParam hanaConnectionParam = new HanaConnectionParam(); + hanaConnectionParam.setJdbcUrl("jdbc:sap://localhost:30015?currentschema=default"); + Assertions.assertEquals( + "jdbc:sap://localhost:30015?currentschema=default&reconnect=true", + hanaDataSourceProcessor.getJdbcUrl(hanaConnectionParam)); + } + + @Test + public void testGetDbType() { + Assertions.assertEquals(DbType.HANA, hanaDataSourceProcessor.getDbType()); + } + + @Test + public void testGetValidationQuery() { + Assertions.assertEquals(DataSourceConstants.HANA_VALIDATION_QUERY, + hanaDataSourceProcessor.getValidationQuery()); + } + + @Test + public void testGetDatasourceUniqueId() { + HanaConnectionParam mysqlConnectionParam = new HanaConnectionParam(); + mysqlConnectionParam.setJdbcUrl("jdbc:sap://localhost:30015?currentschema=default"); + mysqlConnectionParam.setUser("root"); + mysqlConnectionParam.setPassword("123456"); + try (MockedStatic mockedPasswordUtils = Mockito.mockStatic(PasswordUtils.class)) { + Mockito.when(PasswordUtils.encodePassword(Mockito.anyString())).thenReturn("123456"); + Assertions.assertEquals("hana@root@123456@jdbc:sap://localhost:30015?currentschema=default", + hanaDataSourceProcessor.getDatasourceUniqueId(mysqlConnectionParam, DbType.HANA)); + } + } +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/provider/JDBCDataSourceProviderTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/provider/JDBCDataSourceProviderTest.java new file mode 100644 index 0000000000..cea4282e75 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/provider/JDBCDataSourceProviderTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.provider; + +import com.zaxxer.hikari.HikariDataSource; +import org.apache.dolphinscheduler.plugin.datasource.api.provider.JDBCDataSourceProvider; +import org.apache.dolphinscheduler.plugin.datasource.hana.param.HanaConnectionParam; +import org.apache.dolphinscheduler.spi.enums.DbType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class JDBCDataSourceProviderTest { + + @Test + public void testCreateJdbcDataSource() { + try ( + MockedStatic mockedJDBCDataSourceProvider = + Mockito.mockStatic(JDBCDataSourceProvider.class)) { + HikariDataSource dataSource = Mockito.mock(HikariDataSource.class); + mockedJDBCDataSourceProvider + .when(() -> JDBCDataSourceProvider.createJdbcDataSource(Mockito.any(), Mockito.any())) + .thenReturn(dataSource); + Assertions.assertNotNull( + JDBCDataSourceProvider.createJdbcDataSource(new HanaConnectionParam(), DbType.HANA)); + } + } + + @Test + public void testCreateOneSessionJdbcDataSource() { + try ( + MockedStatic mockedJDBCDataSourceProvider = + Mockito.mockStatic(JDBCDataSourceProvider.class)) { + HikariDataSource dataSource = Mockito.mock(HikariDataSource.class); + mockedJDBCDataSourceProvider + .when(() -> JDBCDataSourceProvider.createOneSessionJdbcDataSource(Mockito.any(), Mockito.any())) + .thenReturn(dataSource); + Assertions.assertNotNull( + JDBCDataSourceProvider.createOneSessionJdbcDataSource(new HanaConnectionParam(), DbType.HANA)); + } + } + +} diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/utils/DataSourceUtilsTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/utils/DataSourceUtilsTest.java new file mode 100644 index 0000000000..e7cf949e31 --- /dev/null +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hana/src/test/java/org/apache/dolphinscheduler/plugin/datasource/hana/utils/DataSourceUtilsTest.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.dolphinscheduler.plugin.datasource.hana.utils; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.datasource.api.plugin.DataSourceClientProvider; +import org.apache.dolphinscheduler.plugin.datasource.api.utils.CommonUtils; +import org.apache.dolphinscheduler.plugin.datasource.api.utils.DataSourceUtils; +import org.apache.dolphinscheduler.plugin.datasource.api.utils.PasswordUtils; +import org.apache.dolphinscheduler.plugin.datasource.hana.param.HanaConnectionParam; +import org.apache.dolphinscheduler.plugin.datasource.hana.param.HanaDataSourceParamDTO; +import org.apache.dolphinscheduler.spi.datasource.ConnectionParam; +import org.apache.dolphinscheduler.spi.enums.DbType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.sql.Connection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +@ExtendWith(MockitoExtension.class) +public class DataSourceUtilsTest { + + @Test + public void testCheckDatasourceParam() { + HanaDataSourceParamDTO hanaDataSourceParamDTO = new HanaDataSourceParamDTO(); + hanaDataSourceParamDTO.setHost("localhost"); + hanaDataSourceParamDTO.setDatabase("default"); + Map other = new HashMap<>(); + other.put("reconnect", "true"); + hanaDataSourceParamDTO.setOther(other); + DataSourceUtils.checkDatasourceParam(hanaDataSourceParamDTO); + Assertions.assertTrue(true); + } + + @Test + public void testBuildConnectionParams() { + HanaDataSourceParamDTO hanaDataSourceParamDTO = new HanaDataSourceParamDTO(); + hanaDataSourceParamDTO.setHost("localhost"); + hanaDataSourceParamDTO.setDatabase("default"); + hanaDataSourceParamDTO.setUserName("root"); + hanaDataSourceParamDTO.setPort(30015); + hanaDataSourceParamDTO.setPassword("123456"); + + try ( + MockedStatic mockedStaticPasswordUtils = Mockito.mockStatic(PasswordUtils.class); + MockedStatic mockedStaticCommonUtils = Mockito.mockStatic(CommonUtils.class)) { + mockedStaticPasswordUtils.when(() -> PasswordUtils.encodePassword(Mockito.anyString())) + .thenReturn("123456"); + mockedStaticCommonUtils.when(CommonUtils::getKerberosStartupState).thenReturn(false); + ConnectionParam connectionParam = DataSourceUtils.buildConnectionParams(hanaDataSourceParamDTO); + Assertions.assertNotNull(connectionParam); + } + } + + @Test + public void testBuildConnectionParams2() { + HanaDataSourceParamDTO hanaDatasourceParamDTO = new HanaDataSourceParamDTO(); + hanaDatasourceParamDTO.setHost("localhost"); + hanaDatasourceParamDTO.setDatabase("default"); + hanaDatasourceParamDTO.setUserName("root"); + hanaDatasourceParamDTO.setPort(30015); + hanaDatasourceParamDTO.setPassword("123456"); + ConnectionParam connectionParam = + DataSourceUtils.buildConnectionParams(DbType.HANA, JSONUtils.toJsonString(hanaDatasourceParamDTO)); + Assertions.assertNotNull(connectionParam); + } + + @Test + public void testGetConnection() throws ExecutionException { + try ( + MockedStatic mockedStaticPropertyUtils = Mockito.mockStatic(PropertyUtils.class); + MockedStatic mockedStaticDataSourceClientProvider = + Mockito.mockStatic(DataSourceClientProvider.class)) { + mockedStaticPropertyUtils.when(() -> PropertyUtils.getLong("kerberos.expire.time", 24L)).thenReturn(24L); + DataSourceClientProvider clientProvider = Mockito.mock(DataSourceClientProvider.class); + mockedStaticDataSourceClientProvider.when(DataSourceClientProvider::getInstance).thenReturn(clientProvider); + + Connection connection = Mockito.mock(Connection.class); + Mockito.when(clientProvider.getConnection(Mockito.any(), Mockito.any())).thenReturn(connection); + + HanaConnectionParam connectionParam = new HanaConnectionParam(); + connectionParam.setUser("root"); + connectionParam.setPassword("123456"); + connection = DataSourceClientProvider.getInstance().getConnection(DbType.HANA, connectionParam); + + Assertions.assertNotNull(connection); + } + } + + @Test + public void testGetJdbcUrl() { + HanaConnectionParam hanaConnectionParam = new HanaConnectionParam(); + hanaConnectionParam.setJdbcUrl("jdbc:sap://localhost:30015"); + String jdbcUrl = DataSourceUtils.getJdbcUrl(DbType.HANA, hanaConnectionParam); + Assertions.assertEquals( + "jdbc:sap://localhost:30015?reconnect=true", + jdbcUrl); + } + + @Test + public void testBuildDatasourceParamDTO() { + HanaConnectionParam connectionParam = new HanaConnectionParam(); + connectionParam.setJdbcUrl( + "jdbc:sap://localhost:30015?reconnect=true"); + connectionParam.setAddress("jdbc:mysql://localhost:30015"); + connectionParam.setUser("root"); + connectionParam.setPassword("123456"); + + Assertions.assertNotNull( + DataSourceUtils.buildDatasourceParamDTO(DbType.HANA, JSONUtils.toJsonString(connectionParam))); + + } + + @Test + public void testGetDatasourceProcessor() { + Assertions.assertNotNull(DataSourceUtils.getDatasourceProcessor(DbType.HANA)); + } + + @Test + public void testGetDatasourceProcessorError() { + Assertions.assertThrows(Exception.class, () -> { + DataSourceUtils.getDatasourceProcessor(null); + }); + } +} diff --git a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/DbType.java b/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/DbType.java index 1931f64015..a937713f6b 100644 --- a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/DbType.java +++ b/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/DbType.java @@ -49,7 +49,8 @@ public enum DbType { KYUUBI(18, "kyuubi"), DATABEND(19, "databend"), SNOWFLAKE(20, "snowflake"), - VERTICA(21, "vertica"); + VERTICA(21, "vertica"), + HANA(22, "hana"); private static final Map DB_TYPE_MAP = Arrays.stream(DbType.values()).collect(toMap(DbType::getCode, Functions.identity())); diff --git a/dolphinscheduler-ui/src/service/modules/data-source/types.ts b/dolphinscheduler-ui/src/service/modules/data-source/types.ts index 3ee3e0b070..a493f4119a 100644 --- a/dolphinscheduler-ui/src/service/modules/data-source/types.ts +++ b/dolphinscheduler-ui/src/service/modules/data-source/types.ts @@ -36,6 +36,7 @@ type IDataBase = | 'SSH' | 'DATABEND' | 'SNOWFLAKE' + | 'HANA' type IDataBaseLabel = | 'MYSQL' diff --git a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts index 8d370a3048..06d7ca3e0e 100644 --- a/dolphinscheduler-ui/src/views/datasource/list/use-form.ts +++ b/dolphinscheduler-ui/src/views/datasource/list/use-form.ts @@ -396,6 +396,11 @@ export const datasourceType: IDataBaseOptionKeys = { value: 'DATABEND', label: 'DATABEND', defaultPort: 8000 + }, + HANA: { + value: 'HANA', + label: 'HANA', + defaultPort: 30015 } } diff --git a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts index bec381c55b..9d6bfba71d 100644 --- a/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts +++ b/dolphinscheduler-ui/src/views/projects/task/components/node/fields/use-datasource.ts @@ -127,6 +127,11 @@ export function useDatasource( id: 21, code: 'VERTICA', disabled: false + }, + { + id: 22, + code: 'HANA', + disabled: false } ]