Skip to content

Commit

Permalink
Connects MySQL database using SSH and execute queries
Browse files Browse the repository at this point in the history
  • Loading branch information
akhil-testsigma committed Jan 9, 2025
1 parent d222b47 commit ed0d5e1
Show file tree
Hide file tree
Showing 3 changed files with 330 additions and 0 deletions.
106 changes: 106 additions & 0 deletions mysql_connection_using_ssh/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.testsigma.addons</groupId>
<artifactId>mysql_connection_using_ssh</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<testsigma.sdk.version>1.2.18_cloud</testsigma.sdk.version>
<junit.jupiter.version>5.8.0-M1</junit.jupiter.version>
<testsigma.addon.maven.plugin>1.0.0</testsigma.addon.maven.plugin>
<maven.source.plugin.version>3.2.1</maven.source.plugin.version>
<lombok.version>1.18.20</lombok.version>

</properties>

<dependencies>
<dependency>
<groupId>com.testsigma</groupId>
<artifactId>testsigma-java-sdk</artifactId>
<version>${testsigma.sdk.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.14.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.appium/java-client -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>9.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
</dependencies>
<build>
<finalName>mysql_connection_using_ssh</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven.source.plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package com.testsigma.addons.web;

import com.jcraft.jsch.*;
import com.testsigma.sdk.ApplicationType;
import com.testsigma.sdk.Result;
import com.testsigma.sdk.WebAction;
import com.testsigma.sdk.annotation.Action;
import com.testsigma.sdk.annotation.RunTimeData;
import com.testsigma.sdk.annotation.TestData;
import lombok.Data;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.openqa.selenium.NoSuchElementException;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

@Data
@Action(actionText = "SSH: Execute MySQL Query via SSH Tunnel, SSH Server details: Host: Host-Name, Port: Port-Number, UserName: User-Name, Password: User-Password, Local Port: Local-Port, Remote Host: Remote-Host, Remote Port: Remote-Port, DB User: DB-User, DB Password: DB-Password, DB Name: DB-Name, SQL Query: SQL-Query, Store result in a runtime variable variable-name",
description = "Creates an SSH tunnel, establishes a MySQL connection, and executes a MySQL query.Host-Name: The hostname or IP address of your SSH server. Port-Number: The SSH port number (usually 22). User-Name: The SSH username. User-Password: The SSH password.Local-Port: The local port on your machine that will be used for the tunnel (e.g., 3307). Remote-Host: The hostname or IP address of your MySQL server (often localhost if the database is on the same machine as the SSH server).Remote-Port: The port number of your MySQL server (usually 3306). DB-User: The MySQL database username.DB-Password: The MySQL database password.DB-Name: The MySQL database name.",
applicationType = ApplicationType.WEB,
useCustomScreenshot = false)
public class SDFSSHMySQLQueryExecution extends WebAction {

@TestData(reference = "Host-Name")
private com.testsigma.sdk.TestData hostName;
@TestData(reference = "Port-Number")
private com.testsigma.sdk.TestData portNumber;
@TestData(reference = "User-Name")
private com.testsigma.sdk.TestData userName;
@TestData(reference = "User-Password")
private com.testsigma.sdk.TestData userPassword;
@TestData(reference = "Local-Port")
private com.testsigma.sdk.TestData localPort;
@TestData(reference = "Remote-Host")
private com.testsigma.sdk.TestData remoteHost;
@TestData(reference = "Remote-Port")
private com.testsigma.sdk.TestData remotePort;
@TestData(reference = "DB-User")
private com.testsigma.sdk.TestData dBUsername;
@TestData(reference = "DB-Password")
private com.testsigma.sdk.TestData dBPassword;
@TestData(reference = "DB-Name")
private com.testsigma.sdk.TestData databaseName;
@TestData(reference = "SQL-Query")
private com.testsigma.sdk.TestData sqlQuery;
@TestData(reference = "variable-name")
private com.testsigma.sdk.TestData storeVariable;
@RunTimeData
private com.testsigma.sdk.RunTimeData runTimeData;

private Session session = null;
private Connection conn = null;

@Override
public Result execute() throws NoSuchElementException {
logger.info("Initiating single MySQL query execution via SSH tunnel.");

String sshHost = hostName.getValue().toString();
String sshUser = userName.getValue().toString();
String sshPassword = userPassword.getValue().toString();
int sshPort = Integer.parseInt(portNumber.getValue().toString());
int lport = Integer.parseInt(localPort.getValue().toString());
int rport = Integer.parseInt(remotePort.getValue().toString());
String rhost = remoteHost.getValue().toString();
String dbuserName = dBUsername.getValue().toString();
String dbpassword = dBPassword.getValue().toString();
String dbName = databaseName.getValue().toString();
String query = sqlQuery.getValue().toString();


String url = "jdbc:mysql://localhost:" + lport + "/" + dbName + "?useSSL=false";

try {
// Establish SSH Tunnel and Database Connection
if (!establishSshTunnel(sshHost, sshUser, sshPassword, sshPort, lport, rhost, rport)) {
return Result.FAILED; // Error already set within establishSshTunnel
}


if (!establishDatabaseConnection(url, dbuserName, dbpassword)) {
return Result.FAILED; // Error already set within establishDatabaseConnection
}


// Execute SQL query
logger.info("query : " + query);
String queryResult = executeQuery(query);

if (queryResult.startsWith("Error executing SQL query")) {
return Result.FAILED; // Error already set within executeQuery
}

setSuccessMessage("SQL query executed successfully via SSH tunnel. Result: " + queryResult);
runTimeData.setKey(storeVariable.getValue().toString());
runTimeData.setValue(queryResult);
return Result.SUCCESS;

} finally {
closeResources();
}
}

private boolean establishSshTunnel(String sshHost, String sshUser, String sshPassword, int sshPort, int lport, String rhost, int rport) {
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
session = jsch.getSession(sshUser, sshHost, sshPort);
session.setPassword(sshPassword);
session.setConfig(config);
session.connect(30000);
session.setPortForwardingL(lport, rhost, rport);
return true;
} catch (JSchException e) {
String errorStack = ExceptionUtils.getStackTrace(e);
logger.warn("Error establishing SSH tunnel: " + errorStack);
setErrorMessage("Error establishing SSH tunnel: " + e.getMessage());
return false;
}
}

private boolean establishDatabaseConnection(String url, String dbuserName, String dbpassword) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(url, dbuserName, dbpassword);
if (conn == null || conn.isClosed()){
setErrorMessage("Database connection failed.");
return false;
}
return true;
} catch (ClassNotFoundException e) {
logger.warn("MySQL JDBC Driver not found: " + ExceptionUtils.getStackTrace(e));
setErrorMessage("MySQL JDBC Driver not found: " + e.getMessage());
return false;
}
catch (SQLException e) {
logger.warn("Error establishing database connection: " + ExceptionUtils.getStackTrace(e));
setErrorMessage("Error establishing database connection: " + e.getMessage());
return false;
}
}

private String executeQuery(String query) {
Statement statement = null;
StringBuilder resultString = new StringBuilder();
try {
statement = conn.createStatement();
String trimmedQuery = query.trim();
if (!trimmedQuery.isEmpty()) {
boolean isResultSet = statement.execute(trimmedQuery);
if (isResultSet) {
ResultSet resultSet = statement.getResultSet();
List<String> rowValues;
List<List<String>> resultList = new ArrayList<>();
while (resultSet.next()){
rowValues = new ArrayList<>();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++){
rowValues.add(resultSet.getString(i));
}
resultList.add(rowValues);
}
// Convert resultList to a string
for (List<String> row : resultList) {
resultString.append(String.join(",", row)).append("\n"); // Use a comma to separate columns in a row
}
if (resultString.length() > 0) {
//Remove the last appended new line character
resultString.deleteCharAt(resultString.length() - 1);
}

}else {
// Log the update count
int updateCount = statement.getUpdateCount();
resultString.append("Update count: ").append(updateCount);
if (updateCount == 0) {
resultString.append(" , Query doesn't affect any rows or is a DDL query" );
}
}
}

} catch (SQLException e) {
String errorStack = ExceptionUtils.getStackTrace(e);
//Check if the SQL Exception is due to a syntax error
if (e.getSQLState() != null && e.getSQLState().startsWith("42")) {
logger.warn("SQL Syntax Error: " + errorStack);
setErrorMessage("SQL Syntax Error: " + e.getMessage());
return "Error executing SQL query:" + e.getMessage();
}
logger.warn("Error occurred while executing query: " + errorStack);
setErrorMessage("Error executing SQL query: " + e.getMessage());
return "Error executing SQL query:" + e.getMessage();
} finally {
try {
if (statement != null && !statement.isClosed()) {
statement.close();
}
} catch (SQLException e) {
logger.warn("Error closing statement: "+e.getMessage());
setErrorMessage("Error closing statement: "+e.getMessage()); // Log but don't fail the query because we already got data or error.
}
}
return resultString.toString();
}

private void closeResources() {
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
logger.warn("Error closing database connection: " + e.getMessage());
setErrorMessage("Error closing database connection: " + e.getMessage());
}

if (session != null && session.isConnected()) {
session.disconnect();
}

}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testsigma-sdk.api.key=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMjMyMmM2Ni04NWYzLWIyN2UtN2FiOS0zM2U2M2Q4OWM1MGIiLCJ1bmlxdWVJZCI6IjQxMzgiLCJpZGVudGl0eUFjY291bnRVVUlkIjoiMzUifQ.uBcIb0_GlFYyB70kSv3vT_hZETRi006nbNXepH-YvPbst0gtwjbTUfvkAuF6eum1xCRINY46O8wVeCv8jkMw6Q

0 comments on commit ed0d5e1

Please sign in to comment.