All Downloads are FREE. Search and download functionalities are using the official Maven repository.

core.apiCore.interfaces.SqlInterface Maven / Gradle / Ivy

package core.apiCore.interfaces;

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;

import core.apiCore.ServiceManager;
import core.apiCore.helpers.ConnectionHelper;
import core.apiCore.helpers.DataHelper;
import core.apiCore.helpers.SqlHelper;
import core.helpers.Helper;
import core.helpers.StopWatchHelper;
import core.support.configReader.Config;
import core.support.logger.TestLog;
import core.support.objects.DatabaseObject;
import core.support.objects.KeyValue;
import core.support.objects.ServiceObject;
import core.support.objects.TestObject;
import core.uiCore.driverProperties.globalProperties.CrossPlatformProperties;

/**
 * @author ehsan.matean
 *
 */
public class SqlInterface {

	private static final String SQL_PREFIX = "db.";

	public static final String SQL_CURRENT_DATABASE = "db.current.database";

	private static final String OPTION_DATABASE = "database";

	/**
	 *
	 * interface for database calls
	 * 
	 * @param serviceObject
	 * @return
	 * @throws Exception
	 */
	public static ResultSet DataBaseInterface(ServiceObject serviceObject) throws Exception {

		// get and keep track of all the databases from config
		setDatabaseMap();

		// set default database at position 0(no position) or 1(position 1)
		setDefaultDatabase();

		// evaluate options
		evaluateOption(serviceObject);

		// connect to db
		connectDB();

		// evaluate the response
		ResultSet resSet = evaluateRequestAndValidateResponse(serviceObject);

		return resSet;
	}

	/**
	 * connect to database based on the current database
	 */
	public synchronized static void connectDB() {

		DatabaseObject currentDb = (DatabaseObject) Config.getObjectValue(SQL_CURRENT_DATABASE);

		if (currentDb.getConnection() == null) {
			try {

				// connect through ssh if set in api config
				ConnectionHelper.sshConnect();

				// Register JDBC driver
				String SQLDriver = currentDb.getDriver();
				Class.forName(SQLDriver);

				// connect to db
				String dbURL = currentDb.getUrl();
				String dbName = currentDb.getDatabaseName();

				// set database connection info
				String connectionString = dbURL + "/" + dbName;
				String dbUserName = currentDb.getUsername();
				String dbPassword = currentDb.getPassword();
				TestLog.logPass("db connection: " + connectionString);
				TestLog.logPass("db username: " + dbUserName);
				TestLog.logPass("db password: " + dbPassword);

				// conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/testdb",
				// "postgres", "123");
				currentDb.withConnection(DriverManager.getConnection(connectionString, dbUserName, dbPassword));
				Helper.wait.waitForSeconds(1);
			} catch (Exception e) {
				TestLog.logPass("sql connection failed: " + e.getMessage());
				e.printStackTrace();
				Helper.assertTrue("sql connection failed", false);
			}
		}
	}

	public static void evaluateOption(ServiceObject serviceObject) {

		// reset validation timeout. will be overwritten by option value if set
		resetValidationTimeout();

		// if no option specified
		if (serviceObject.getOption().isEmpty()) {
			return;
		}

		// replace parameters for request body
		serviceObject.withOption(DataHelper.replaceParameters(serviceObject.getOption()));

		// get key value mapping of header parameters
		List keywords = DataHelper.getValidationMap(serviceObject.getOption());

		// iterate through key value pairs for headers, separated by ";"
		for (KeyValue keyword : keywords) {

			// if additional options
			switch (keyword.key.toLowerCase()) {
			case OPTION_DATABASE:
				int position = Helper.getIntFromString(keyword.value.toString(), true);
				if (DatabaseObject.DATABASES.get(position) == null)
					Helper.assertFalse("database number: " + position + " not found");
				DatabaseObject database = DatabaseObject.DATABASES.get(position);
				Config.putValue(SQL_CURRENT_DATABASE, database, false);
				break;
			case ServiceManager.OPTION_NO_VALIDATION_TIMEOUT:
				Config.putValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_ENABLED, false);
				break;

			case ServiceManager.OPTION_WAIT_FOR_RESPONSE:
				Config.putValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_ENABLED, false);
				Config.putValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_SECONDS, keyword.value, false);
				break;
			case ServiceManager.OPTION_WAIT_FOR_RESPONSE_DELAY:
				Config.putValue(ServiceManager.SERVICE_RESPONSE_DELAY_BETWEEN_ATTEMPTS_SECONDS, keyword.value, false);
				break;
			default:
				break;
			}
		}
		KeyValue.printKeyValue(keywords, "option");
	}

	/**
	 * set database values from config into database object hashmap format:
	 * db.1.driver = org.postgresql.Driver 1 is position driver is key (command)
	 * org.postgresql.Driver is value
	 */
	public static void setDatabaseMap() {

		// if already set,
		if (DatabaseObject.DATABASES.size() > 0)
			return;

		// get all keys from config
		Map propertiesMap = TestObject.getTestInfo().config;

		// load config/properties values from entries with "db." prefix
		for (Entry entry : propertiesMap.entrySet()) {

			// if starts with chrome.options or firefox.options prefix
			boolean isDatabase = entry.getKey().toString().startsWith(SQL_PREFIX);

			// format db.position.command = value. eg. db.1.driver = org.postgresql.Driver
			if (isDatabase) {
				String fullKey = entry.getKey().toString();
				int position = Helper.getIntFromString(fullKey);

				// if position is not set, set to 0 (default)
				String splitter = SQL_PREFIX + position + ".";
				if (position == -1) {
					position = 0;
					splitter = SQL_PREFIX;
				}
				String[] split = fullKey.split(splitter);

				if (split.length == 1)
					continue;

				String command = split[1].trim();
				String value = entry.getValue().toString().trim();

				// set database object in global hashmap
				setDatabaseObject(command, position, value);
			}
		}
	}

	/**
	 * set default database to be from position 0 or 1 if db 0 is set, set as
	 * default else set db 1 fail if no position 0 or 1 is not set
	 */
	public static void setDefaultDatabase() {

		if (DatabaseObject.DATABASES.get(0) != null && !DatabaseObject.DATABASES.get(0).getDriver().isEmpty()) {
			Config.putValue(SQL_CURRENT_DATABASE, DatabaseObject.DATABASES.get(0));
		} else if (DatabaseObject.DATABASES.get(1) != null) {
			Config.putValue(SQL_CURRENT_DATABASE, DatabaseObject.DATABASES.get(1));
		} else {
			Helper.assertFalse(
					"database position must be set. eg. db.1.driver = value where 1 is position of database ");
		}
	}

	/**
	 * set database object in hashmap key: position value: database object
	 * 
	 * @param command
	 * @param position
	 * @param value
	 */
	public static void setDatabaseObject(String command, int position, String value) {
		DatabaseObject database = null;
		if (DatabaseObject.DATABASES.get(position) == null)
			database = new DatabaseObject();
		else
			database = DatabaseObject.DATABASES.get(position);

		switch (command) {
		case "driver":
			database.withDriver(value);
			break;
		case "url":
			database.withUrl(value);
			break;
		case "name":
			database.withDatabaseName(value);
			break;
		case "username":
			database.withUsername(value);
			break;
		case "password":
			database.withPassword(value);
			break;
		default:

		}

		DatabaseObject.DATABASES.put(position, database);
	}

	/**
	 * evaluaes the sql statement
	 * 
	 * @param serviceObject
	 * @return
	 * @throws Exception
	 */
	public static ResultSet evaluateDbQuery(ServiceObject serviceObject) throws Exception {

		// replace parameters for request body, including template file (json, xml, or
		// other)
		serviceObject.withRequestBody(DataHelper.getRequestBodyIncludingTemplate(serviceObject));

		// execute query
		String sql = serviceObject.getRequestBody();
		TestLog.logPass("sql statement: " + sql);

		DatabaseObject currentDb = (DatabaseObject) Config.getObjectValue(SQL_CURRENT_DATABASE);
		PreparedStatement sqlStmt = currentDb.getConnection().prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE,
				ResultSet.CONCUR_UPDATABLE);

		// execute And wait for response if expected values are set
		ResultSet resSet = null;
		try {
			resSet = executeAndWaitForDbResponse(sqlStmt, serviceObject);
		} catch (Exception e) {
			e.printStackTrace();
			Helper.assertFalse(e.getMessage());
		}

		return resSet;
	}

	/**
	 * evaluate the response
	 * 
	 * @param serviceObject
	 * @param resSet
	 * @throws Exception
	 */
	public static List evaluateReponse(ServiceObject serviceObject, ResultSet resSet) throws Exception {

		List errorMessages = new ArrayList();

		// return if expected response is empty
		if (serviceObject.getExpectedResponse().isEmpty() && serviceObject.getOutputParams().isEmpty())
			return errorMessages;

		// fail test if no results returned
		if (!resSet.isBeforeFirst()) {
			Helper.assertTrue("no results returned from db query", false);
		}

		// get the first result row
		resSet.next();

		// saves response values to config object
		SqlHelper.saveOutboundSQLParameters(resSet, serviceObject.getOutputParams());

		errorMessages = validateExpectedResponse(serviceObject.getExpectedResponse(), resSet);

		// Clean-up environment
		resSet.close();

		// remove all empty response strings
		errorMessages = DataHelper.removeEmptyElements(errorMessages);
		return errorMessages;
	}

	/**
	 * executes And waits for response calls the query in each loop does not wait if
	 * expected or partial expected response are empty
	 * 
	 * @param sqlStmt
	 * @param serviceObject
	 * @return
	 * @throws SQLException
	 */
	public static ResultSet executeAndWaitForDbResponse(PreparedStatement sqlStmt, ServiceObject serviceObject)
			throws SQLException {
		int timeout = CrossPlatformProperties.getGlobalTimeout();
		ResultSet resSet;
		StopWatchHelper watch = StopWatchHelper.start();
		boolean messageReceived = false;
		long passedTimeInSeconds = 0;
		do {
			sqlStmt.execute();
			resSet = sqlStmt.getResultSet();

			// if no response expected, do not wait for response
			if (serviceObject.getExpectedResponse().isEmpty())
				return resSet;

			if (resSet.isBeforeFirst()) {
				messageReceived = true;
				return resSet;
			}
			Helper.wait.waitForSeconds(1);
			passedTimeInSeconds = watch.time(TimeUnit.SECONDS);
		} while (!messageReceived && passedTimeInSeconds < timeout);

		return resSet;
	}

	public static List validateExpectedResponse(String expected, ResultSet resSet) throws SQLException {

		List errorMessages = new ArrayList();

		if (expected.isEmpty())
			return errorMessages;

		// validate response body against expected string
		expected = DataHelper.replaceParameters(expected);
		TestLog.logPass("expected result: " + Helper.stringRemoveLines(expected));

		// separate the expected response by &&
		String[] criteria = expected.split("&&");
		for (String criterion : criteria) {
			if (SqlHelper.isValidJson(criterion)) {
				SqlHelper.validateByJsonBody(criterion, resSet);
			} else {
				List keywords = DataHelper.getValidationMap(expected);
				errorMessages.addAll(SqlHelper.validateSqlKeywords(keywords, resSet));
			}
		}

		return errorMessages;
	}

	/**
	 * evaluate request and validate response retry until validation timeout period
	 * in seconds
	 * 
	 * @param serviceObject
	 * @return
	 * @throws Exception
	 */
	public static ResultSet evaluateRequestAndValidateResponse(ServiceObject serviceObject) throws Exception {
		List errorMessages = new ArrayList();
		ResultSet resSet = null;

		StopWatchHelper watch = StopWatchHelper.start();
		long passedTimeInSeconds = 0;

		boolean isValidationTimeout = Config.getBooleanValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_ENABLED);
		int maxRetrySeconds = Config.getIntValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_SECONDS);
		int currentRetryCount = 0;

		do {

			// evaluate the sql query
			resSet = evaluateDbQuery(serviceObject);

			// evaluate the response
			errorMessages = evaluateReponse(serviceObject, resSet);

			// if validation timeout is not enabled, break out of the loop
			if (!isValidationTimeout)
				break;

			if (currentRetryCount > 0) {
				int waitTime = Config.getIntValue(ServiceManager.SERVICE_RESPONSE_DELAY_BETWEEN_ATTEMPTS_SECONDS);
				double waitMultiplier = Config.getDoubleValue(ServiceManager.SERVICE_RESPONSE_DELAY_BETWEEN_ATTEMPTS_MULTIPLIER);
				
				double waitTimeSeconds = waitTime * Math.pow(waitMultiplier, currentRetryCount - 1);
				Helper.waitForSeconds(waitTimeSeconds);
				TestLog.ConsoleLog("attempt #" + (currentRetryCount) + " waiting seconds: " + waitTimeSeconds);
				
				String errors = StringUtils.join(errorMessages, "\n error: ");
				TestLog.ConsoleLog("attempt failed with message: " + errors);
				TestLog.ConsoleLog("attempt #" + (currentRetryCount + 1));

			}
			currentRetryCount++;

			passedTimeInSeconds = watch.time(TimeUnit.SECONDS);

		} while (!errorMessages.isEmpty() && passedTimeInSeconds < maxRetrySeconds);

		if (!errorMessages.isEmpty()) {
			TestLog.ConsoleLog("Validation failed after: " + passedTimeInSeconds + " seconds with " + currentRetryCount + " retries");
			String errorString = StringUtils.join(errorMessages, "\n error: ");
			TestLog.ConsoleLog(errorString);
			Helper.assertFalse(StringUtils.join(errorMessages, "\n error: "));
		}

		return resSet;
	}

	/**
	 * reset validation timeout
	 */
	private static void resetValidationTimeout() {
		// reset validation timeout option
		String defaultValidationTimeoutIsEnabled = Config
				.getGlobalValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_ENABLED);
		int defaultValidationTimeoutIsSeconds = Config
				.getGlobalIntValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_SECONDS);
		if (defaultValidationTimeoutIsSeconds == -1)
			defaultValidationTimeoutIsSeconds = 60;

		// delay timeout reset
		int defaultValidationTimeoutDelay = Config
				.getGlobalIntValue(ServiceManager.SERVICE_RESPONSE_DELAY_BETWEEN_ATTEMPTS_SECONDS);
		if (defaultValidationTimeoutDelay == -1)
			defaultValidationTimeoutDelay = 3;

		Config.putValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_ENABLED, defaultValidationTimeoutIsEnabled, false);
		Config.putValue(ServiceManager.SERVICE_TIMEOUT_VALIDATION_SECONDS, defaultValidationTimeoutIsSeconds, false);
		Config.putValue(ServiceManager.SERVICE_RESPONSE_DELAY_BETWEEN_ATTEMPTS_SECONDS, defaultValidationTimeoutDelay, false);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy