From 17aeafa678d585a478725e404dcbdb4af5b61086 Mon Sep 17 00:00:00 2001 From: Aymeric <aymeric@firebolt.io> Date: Wed, 22 Mar 2023 13:43:08 +0000 Subject: [PATCH 01/15] Start adding support for new identity --- .github/workflows/integration-test.yml | 7 +- .../java/integration/ConnectionInfo.java | 6 +- .../java/integration/IntegrationTest.java | 24 +- .../tests/PreparedStatementTest.java | 5 +- .../java/integration/tests/StatementTest.java | 8 + .../java/integration/tests/TimestampTest.java | 12 + .../java/com/firebolt/FireboltDriver.java | 2 +- src/main/java/com/firebolt/jdbc/Query.java | 65 ------ .../java/com/firebolt/jdbc/QueryResult.java | 5 +- .../firebolt/jdbc/client/FireboltClient.java | 5 + .../client/account/FireboltAccountClient.java | 133 ----------- .../AuthenticationRequestFactory.java | 18 -- .../FireboltAuthenticationClient.java | 5 +- .../ServiceAccountAuthenticationRequest.java | 39 ++-- ...UsernamePasswordAuthenticationRequest.java | 35 --- .../gateway/FireboltGatewayUrlClient.java | 38 ++++ .../client/gateway/GatewayUrlResponse.java | 15 ++ .../client/query/StatementClientImpl.java | 5 +- .../jdbc/connection/FireboltConnection.java | 75 +++++-- .../jdbc/connection/FireboltJdbcUrlUtil.java | 52 ----- .../com/firebolt/jdbc/connection/UrlUtil.java | 44 ++++ .../settings/FireboltProperties.java | 37 +++- .../settings/FireboltSessionProperty.java | 6 +- .../firebolt/jdbc/metadata/MetadataUtil.java | 66 +++++- .../jdbc/resultset/FireboltResultSet.java | 2 +- .../FireboltAuthenticationService.java | 2 +- .../jdbc/service/FireboltEngineService.java | 167 +++++++------- .../service/FireboltGatewayUrlService.java | 15 ++ .../service/FireboltStatementService.java | 7 +- .../jdbc/statement/FireboltStatement.java | 2 +- .../com/firebolt/jdbc/util/PropertyUtil.java | 4 +- .../com/firebolt/jdbc/RawStatementTest.java | 1 + .../account/FireboltAccountClientTest.java | 3 + .../AuthenticationRequestFactoryTest.java | 32 --- .../FireboltAuthenticationClientTest.java | 10 +- ...rviceAccountAuthenticationRequestTest.java | 10 +- ...namePasswordAuthenticationRequestTest.java | 40 ---- .../gateway/FireboltGatewayUrlClientTest.java | 72 ++++++ .../client/query/StatementClientImplTest.java | 29 ++- .../connection/FireboltConnectionTest.java | 149 ++++++++----- .../connection/FireboltJdbcUrlUtilTest.java | 25 --- .../firebolt/jdbc/connection/UrlUtilTest.java | 29 +++ .../settings/FireboltPropertiesTest.java | 74 ++++--- .../FireboltAuthenticationServiceTest.java | 16 +- .../service/FireboltEngineServiceTest.java | 206 +++++------------- .../service/FireboltStatementServiceTest.java | 26 +-- .../jdbc/statement/FireboltStatementTest.java | 16 +- .../FireboltPreparedStatementTest.java | 28 +-- .../firebolt/jdbc/util/PropertyUtilTest.java | 5 +- 49 files changed, 802 insertions(+), 875 deletions(-) delete mode 100644 src/main/java/com/firebolt/jdbc/Query.java delete mode 100644 src/main/java/com/firebolt/jdbc/client/account/FireboltAccountClient.java delete mode 100644 src/main/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactory.java delete mode 100644 src/main/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequest.java create mode 100644 src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java create mode 100644 src/main/java/com/firebolt/jdbc/client/gateway/GatewayUrlResponse.java delete mode 100644 src/main/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtil.java create mode 100644 src/main/java/com/firebolt/jdbc/connection/UrlUtil.java create mode 100644 src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java delete mode 100644 src/test/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactoryTest.java delete mode 100644 src/test/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequestTest.java create mode 100644 src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java delete mode 100644 src/test/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtilTest.java create mode 100644 src/test/java/com/firebolt/jdbc/connection/UrlUtilTest.java diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 448169567..21cd1a41a 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,6 +7,9 @@ on: description: 'Database - a new one will be created if not provided' required: false default: '' + account: + description: 'Account' + required: true environment: description: 'Environment to run the tests against' type: choice @@ -66,7 +69,7 @@ jobs: fi - name: Run integration tests - run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Dapi=api.${{ github.event.inputs.environment }}.firebolt.io -Dpassword="${{ env.SERVICE_ACCOUNT_SECRET }}" -Duser="${{ env.SERVICE_ACCOUNT_ID }}" + run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dpassword="${{ env.SERVICE_ACCOUNT_SECRET }}" -Duser="${{ env.SERVICE_ACCOUNT_ID }} -Daccount=${{ github.event.inputs.account }}" - name: "Foresight: Analyze Test Results" uses: runforesight/foresight-test-kit-action@v1 @@ -77,4 +80,4 @@ jobs: test_path: ./build/test-results/ tags: | type:"integration" - language:"Java" + language:"Java" \ No newline at end of file diff --git a/src/integrationTest/java/integration/ConnectionInfo.java b/src/integrationTest/java/integration/ConnectionInfo.java index 9c1e26127..f4ea57ded 100644 --- a/src/integrationTest/java/integration/ConnectionInfo.java +++ b/src/integrationTest/java/integration/ConnectionInfo.java @@ -9,14 +9,16 @@ public class ConnectionInfo { private static ConnectionInfo INSTANCE; String password; String user; - String api; + String env; String database; + String account; private ConnectionInfo() { password = Optional.ofNullable(System.getProperty("password")).map(p -> p.replace("\"", "")).orElse(null); user = Optional.ofNullable(System.getProperty("user")).map(u -> u.replace("\"", "")).orElse(null); - api = System.getProperty("api"); + env = System.getProperty("env"); database = System.getProperty("db"); + account = System.getProperty("account"); } public static ConnectionInfo getInstance() { diff --git a/src/integrationTest/java/integration/IntegrationTest.java b/src/integrationTest/java/integration/IntegrationTest.java index aff322ced..29c0a6ebb 100644 --- a/src/integrationTest/java/integration/IntegrationTest.java +++ b/src/integrationTest/java/integration/IntegrationTest.java @@ -20,27 +20,29 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public abstract class IntegrationTest { + private static final String JDBC_URL_PREFIX = "jdbc:firebolt:"; + protected Connection createLocalConnection(String queryParams) throws SQLException { return DriverManager.getConnection( - "jdbc:firebolt://localhost" + "/" + integration.ConnectionInfo.getInstance().getDatabase() - + queryParams, + JDBC_URL_PREFIX + integration.ConnectionInfo.getInstance().getDatabase() + + queryParams + "&host=localhost" + getAccountParam(), integration.ConnectionInfo.getInstance().getUser(), integration.ConnectionInfo.getInstance().getPassword()); } protected Connection createConnection() throws SQLException { return DriverManager.getConnection( - "jdbc:firebolt://" + integration.ConnectionInfo.getInstance().getApi() + "/" - + integration.ConnectionInfo.getInstance().getDatabase(), + JDBC_URL_PREFIX + + integration.ConnectionInfo.getInstance().getDatabase() + "?" + getEnvParam() + getAccountParam() , integration.ConnectionInfo.getInstance().getUser(), integration.ConnectionInfo.getInstance().getPassword()); } protected Connection createConnection(String engine) throws SQLException { return DriverManager.getConnection( - "jdbc:firebolt://" + integration.ConnectionInfo.getInstance().getApi() + "/" - + integration.ConnectionInfo.getInstance().getDatabase() - + Optional.ofNullable(engine).map(e -> "?engine=" + e).orElse(""), + JDBC_URL_PREFIX + + integration.ConnectionInfo.getInstance().getDatabase() + + Optional.ofNullable(engine).map(e -> "?" + getEnvParam() +"&engine=" + e + getAccountParam() ).orElse("?" + getEnvParam() + getAccountParam()), integration.ConnectionInfo.getInstance().getUser(), integration.ConnectionInfo.getInstance().getPassword()); } @@ -70,4 +72,12 @@ protected void removeExistingClient() throws NoSuchFieldException, IllegalAccess field.set(null, null); } + private String getAccountParam() { + return "&account=" + integration.ConnectionInfo.getInstance().getAccount(); + } + + private String getEnvParam() { + return "&env=" + integration.ConnectionInfo.getInstance().getEnv(); + } + } diff --git a/src/integrationTest/java/integration/tests/PreparedStatementTest.java b/src/integrationTest/java/integration/tests/PreparedStatementTest.java index 2b5e3f39e..4a775cd20 100644 --- a/src/integrationTest/java/integration/tests/PreparedStatementTest.java +++ b/src/integrationTest/java/integration/tests/PreparedStatementTest.java @@ -165,8 +165,9 @@ void shouldFailSQLInjectionAttempt() throws SQLException { private QueryResult createExpectedResult(List<List<?>> expectedRows) { return QueryResult.builder().databaseName(ConnectionInfo.getInstance().getDatabase()) .tableName("prepared_statement_test") - .columns(Arrays.asList(QueryResult.Column.builder().name("sales").type(FireboltDataType.BIG_INT).build(), - QueryResult.Column.builder().name("make").type(FireboltDataType.TEXT).build())) + .columns( + Arrays.asList(QueryResult.Column.builder().name("sales").type(FireboltDataType.BIG_INT).build(), + QueryResult.Column.builder().name("make").type(FireboltDataType.TEXT).build())) .rows(expectedRows).build(); } diff --git a/src/integrationTest/java/integration/tests/StatementTest.java b/src/integrationTest/java/integration/tests/StatementTest.java index 1be46b92e..e9c425595 100644 --- a/src/integrationTest/java/integration/tests/StatementTest.java +++ b/src/integrationTest/java/integration/tests/StatementTest.java @@ -29,6 +29,14 @@ void afterEach() { executeStatementFromFile("/statements/statement/cleanup.sql"); } + @Test + void shouldSelect1() throws SQLException { + try (Connection connection = this.createConnection(); Statement statement = connection.createStatement()) { + statement.executeQuery("SELECT 1;"); + assertNotNull(statement.executeQuery("SELECT 1;")); + } + } + @Test void shouldReuseStatementWhenNotCloseOnCompletion() throws SQLException { try (Connection connection = this.createConnection(); Statement statement = connection.createStatement()) { diff --git a/src/integrationTest/java/integration/tests/TimestampTest.java b/src/integrationTest/java/integration/tests/TimestampTest.java index ee3f671f7..a62b8bb6e 100644 --- a/src/integrationTest/java/integration/tests/TimestampTest.java +++ b/src/integrationTest/java/integration/tests/TimestampTest.java @@ -131,6 +131,18 @@ void shouldRemoveOffsetDIffWhenTimestampOffsetHasChangedCET() throws SQLExceptio } } + @Test + void shouldHaveCorrectInfo() throws SQLException { + try (Connection connection = this.createConnection("system"); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT 3::decimal")) { + resultSet.next(); + assertEquals(9, resultSet.getMetaData().getScale(1)); + assertEquals(38, resultSet.getMetaData().getPrecision(1)); + } + + } + @Test void shouldReturnTimestampFromTimestampntz() throws SQLException { try (Connection connection = this.createConnection(); diff --git a/src/main/java/com/firebolt/FireboltDriver.java b/src/main/java/com/firebolt/FireboltDriver.java index e079e62e6..849e59adb 100644 --- a/src/main/java/com/firebolt/FireboltDriver.java +++ b/src/main/java/com/firebolt/FireboltDriver.java @@ -17,7 +17,7 @@ public class FireboltDriver implements Driver { public static final String JDBC_FIREBOLT = "jdbc:firebolt:"; - private static final String JDBC_FIREBOLT_PREFIX = JDBC_FIREBOLT + "//"; + private static final String JDBC_FIREBOLT_PREFIX = JDBC_FIREBOLT; static { try { diff --git a/src/main/java/com/firebolt/jdbc/Query.java b/src/main/java/com/firebolt/jdbc/Query.java deleted file mode 100644 index aa22b74b5..000000000 --- a/src/main/java/com/firebolt/jdbc/Query.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.firebolt.jdbc; - -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - -import lombok.Builder; -import lombok.Value; - -/** - * Represents a SQL query that can be sent to Firebolt - */ -@Builder -@Value -public class Query { - String select; - String from; - String innerJoin; - String orderBy; - List<String> conditions; - - /** - * Parse the object to a SQL query that can be sent to Firebolt - * - * @return SQL query that can be sent to Firebolt - */ - public String toSql() { - StringBuilder query = new StringBuilder(); - if (StringUtils.isBlank(select)) { - throw new IllegalStateException("Cannot create query: SELECT cannot be blank"); - } - if (StringUtils.isBlank(from)) { - throw new IllegalStateException("Cannot create query: FROM cannot be blank"); - } - - query.append("SELECT ").append(select); - query.append(" FROM ").append(from); - if (StringUtils.isNotBlank(innerJoin)) { - query.append(" JOIN ").append(innerJoin); - } - query.append(getConditionsPart()); - if (StringUtils.isNotBlank(orderBy)) { - query.append(" order by ").append(orderBy); - } - return query.toString(); - } - - private String getConditionsPart() { - StringBuilder agg = new StringBuilder(); - Iterator<String> iter = conditions.iterator(); - if (iter.hasNext()) { - agg.append(" WHERE "); - } - if (iter.hasNext()) { - String entry = iter.next(); - agg.append(entry); - } - while (iter.hasNext()) { - String entry = iter.next(); - agg.append(" AND ").append(entry); - } - return agg.toString(); - } -} diff --git a/src/main/java/com/firebolt/jdbc/QueryResult.java b/src/main/java/com/firebolt/jdbc/QueryResult.java index 0e2ccddc4..bcdb01dbb 100644 --- a/src/main/java/com/firebolt/jdbc/QueryResult.java +++ b/src/main/java/com/firebolt/jdbc/QueryResult.java @@ -13,6 +13,7 @@ /** * Class containing a query result that can be used to create a * {@link com.firebolt.jdbc.resultset.FireboltResultSet} + * It is particularly useful for metadata methods as a ResulSet containing metadata info must be returned. */ @Builder @Value @@ -36,8 +37,8 @@ public String toString() { StringBuilder stringBuilder = new StringBuilder(); this.appendWithListValues(stringBuilder, columns.stream().map(Column::getName).collect(Collectors.toList())); stringBuilder.append(NEXT_LINE); - this.appendWithListValues(stringBuilder, columns.stream().map(Column::getType) - .map(FireboltDataType::getAliases).map( aliases -> aliases[0]).collect(Collectors.toList())); + this.appendWithListValues(stringBuilder, columns.stream().map(Column::getType).map(FireboltDataType::getAliases) + .map(aliases -> aliases[0]).collect(Collectors.toList())); stringBuilder.append(NEXT_LINE); for (int i = 0; i < rows.size(); i++) { diff --git a/src/main/java/com/firebolt/jdbc/client/FireboltClient.java b/src/main/java/com/firebolt/jdbc/client/FireboltClient.java index 5694fbaf1..a520f12f5 100644 --- a/src/main/java/com/firebolt/jdbc/client/FireboltClient.java +++ b/src/main/java/com/firebolt/jdbc/client/FireboltClient.java @@ -46,6 +46,11 @@ protected FireboltClient(OkHttpClient httpClient, FireboltConnection connection, customClients != null ? customClients : ""); } + protected <T> T getResource(String uri, String accessToken, Class<T> valueType) + throws IOException, FireboltException { + return getResource(uri, uri, accessToken, valueType); + } + protected <T> T getResource(String uri, String host, String accessToken, Class<T> valueType) throws IOException, FireboltException { Request rq = createGetRequest(uri, accessToken); diff --git a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountClient.java b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountClient.java deleted file mode 100644 index 0b020a891..000000000 --- a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountClient.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.firebolt.jdbc.client.account; - -import java.io.IOException; - -import org.apache.commons.lang3.StringUtils; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.firebolt.jdbc.client.FireboltClient; -import com.firebolt.jdbc.client.account.response.FireboltAccountResponse; -import com.firebolt.jdbc.client.account.response.FireboltDefaultDatabaseEngineResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineIdResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineResponse; -import com.firebolt.jdbc.connection.FireboltConnection; -import com.firebolt.jdbc.exception.ExceptionType; -import com.firebolt.jdbc.exception.FireboltException; - -import lombok.CustomLog; -import okhttp3.OkHttpClient; - -@CustomLog -public class FireboltAccountClient extends FireboltClient { - - private static final String GET_ACCOUNT_ID_URI = "%s/iam/v2/accounts:getIdByName?accountName=%s"; - private static final String URI_SUFFIX_ENGINE_AND_ACCOUNT_ID_BY_ENGINE_NAME = "engines:getIdByName?engine_name="; - private static final String URI_SUFFIX_ACCOUNT_ENGINE_INFO_BY_ENGINE_ID = "engines/"; - private static final String URI_SUFFIX_DATABASE_INFO_URL = "engines:getURLByDatabaseName?databaseName="; - private static final String URI_PREFIX_WITH_ACCOUNT_RESOURCE = "%s/core/v1/accounts/%s/%s"; - private static final String URI_PREFIX_WITHOUT_ACCOUNT_RESOURCE = "%s/core/v1/account/%s"; - - public FireboltAccountClient(OkHttpClient httpClient, ObjectMapper objectMapper, - FireboltConnection fireboltConnection, String customDrivers, String customClients) { - super(httpClient, fireboltConnection, customDrivers, customClients, objectMapper); - } - - /** - * Returns the account - * - * @param host the host - * @param account the name of the account - * @param accessToken the access token - * @return the account - */ - public FireboltAccountResponse getAccount(String host, String account, String accessToken) - throws FireboltException, IOException { - String uri = String.format(GET_ACCOUNT_ID_URI, host, account); - return getResource(uri, host, accessToken, FireboltAccountResponse.class); - } - - /** - * Returns an engine - * - * @param host the host - * @param accountId the id of the account - * @param engineName the engine name - * @param engineId the engine id - * @param accessToken the access token - * @return the engine - */ - public FireboltEngineResponse getEngine(String host, String accountId, String engineName, String engineId, - String accessToken) throws FireboltException, IOException { - try { - String uri = createAccountUri(accountId, host, URI_SUFFIX_ACCOUNT_ENGINE_INFO_BY_ENGINE_ID + engineId); - return getResource(uri, host, accessToken, FireboltEngineResponse.class); - } catch (FireboltException exception) { - if (exception.getType() == ExceptionType.RESOURCE_NOT_FOUND) { - throw new FireboltException( - String.format("The address of the engine with name %s and id %s could not be found", engineName, - engineId), - exception, ExceptionType.RESOURCE_NOT_FOUND); - } else { - throw exception; - } - } - } - - /** - * Returns the default engine of the database - * - * @param host the host - * @param accountId the account id - * @param dbName the name of the database - * @param accessToken the access token - * @return the default engine for the database - */ - public FireboltDefaultDatabaseEngineResponse getDefaultEngineByDatabaseName(String host, String accountId, String dbName, - String accessToken) throws FireboltException, IOException { - String uri = createAccountUri(accountId, host, URI_SUFFIX_DATABASE_INFO_URL + dbName); - try { - return getResource(uri, host, accessToken, FireboltDefaultDatabaseEngineResponse.class); - - } catch (FireboltException exception) { - if (exception.getType() == ExceptionType.RESOURCE_NOT_FOUND) { - throw new FireboltException(String.format("The database with the name %s could not be found", dbName), - exception, ExceptionType.RESOURCE_NOT_FOUND); - } else { - throw exception; - } - } - } - - /** - * Returns the engine id - * - * @param host the host - * @param accountId the account id - * @param engineName the name of the engine - * @param accessToken the access token - * @return the engine id - */ - public FireboltEngineIdResponse getEngineId(String host, String accountId, String engineName, String accessToken) - throws FireboltException, IOException { - try { - String uri = createAccountUri(accountId, host, - URI_SUFFIX_ENGINE_AND_ACCOUNT_ID_BY_ENGINE_NAME + engineName); - return getResource(uri, host, accessToken, FireboltEngineIdResponse.class); - } catch (FireboltException exception) { - if (exception.getType() == ExceptionType.RESOURCE_NOT_FOUND) { - throw new FireboltException(String.format("The engine %s could not be found", engineName), exception, - ExceptionType.RESOURCE_NOT_FOUND); - } else { - throw exception; - } - } - } - - private String createAccountUri(String account, String host, String suffix) { - if (StringUtils.isNotEmpty(account)) - return String.format(URI_PREFIX_WITH_ACCOUNT_RESOURCE, host, account, suffix); - else - return String.format(URI_PREFIX_WITHOUT_ACCOUNT_RESOURCE, host, suffix); - } - -} diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactory.java b/src/main/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactory.java deleted file mode 100644 index 0f432c707..000000000 --- a/src/main/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactory.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.firebolt.jdbc.client.authentication; - -import org.apache.commons.lang3.StringUtils; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class AuthenticationRequestFactory { - - public static AuthenticationRequest getAuthenticationRequest(String username, String password, String host) { - if (StringUtils.isEmpty(username) || StringUtils.contains(username, "@")) { - return new UsernamePasswordAuthenticationRequest(username, password, host); - } else { - return new ServiceAccountAuthenticationRequest(username, password, host); - } - } - -} diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java b/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java index 3813555a9..a63d00747 100644 --- a/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java +++ b/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java @@ -32,10 +32,9 @@ public FireboltAuthenticationClient(OkHttpClient httpClient, ObjectMapper object * @param password the password * @return the connection tokens */ - public FireboltConnectionTokens postConnectionTokens(String host, String user, String password) + public FireboltConnectionTokens postConnectionTokens(String host, String user, String password, String environment) throws IOException, FireboltException { - AuthenticationRequest authenticationRequest = AuthenticationRequestFactory.getAuthenticationRequest(user, - password, host); + AuthenticationRequest authenticationRequest = new ServiceAccountAuthenticationRequest(user, password, environment); String uri = authenticationRequest.getUri(); log.debug("Creating connection with url {}", uri); Request request = this.createPostRequest(uri, authenticationRequest.getRequestBody()); diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java b/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java index a6bf99182..4819bbc6a 100644 --- a/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java +++ b/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java @@ -1,27 +1,34 @@ package com.firebolt.jdbc.client.authentication; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.AllArgsConstructor; import okhttp3.FormBody; import okhttp3.RequestBody; @AllArgsConstructor public class ServiceAccountAuthenticationRequest implements AuthenticationRequest { - private static final String CLIENT_CREDENTIALS = "client_credentials"; - private static final String GRAND_TYPE_FIELD_NAME = "grant_type"; - private static final String CLIENT_ID_FIELD_NAME = "client_id"; - private static final String CLIENT_SECRET_FIELD_NAME = "client_secret"; - private static final String AUTH_URL = "%s/auth/v1/token"; - private String id; - private String secret; - private String host; - public RequestBody getRequestBody() { - return new FormBody.Builder().add(CLIENT_ID_FIELD_NAME, id).add(CLIENT_SECRET_FIELD_NAME, secret) - .add(GRAND_TYPE_FIELD_NAME, CLIENT_CREDENTIALS).build(); - } + private static final String AUDIENCE_FIELD_NAME = "audience"; + private static final String GRAND_TYPE_FIELD_NAME = "grant_type"; + private static final String GRAND_TYPE_FIELD_VALUE = "client_credentials"; + private static final String CLIENT_ID_FIELD_NAME = "client_id"; + private static final String CLIENT_SECRET_FIELD_NAME = "client_secret"; + private static final String AUTH_URL = "https://id.%s.firebolt.io/oauth/token"; - @Override - public String getUri() { - return String.format(AUTH_URL, host); - } + private final String clientId; + private final String clientSecret; + private final String environment; + + @Override + public RequestBody getRequestBody() throws JsonProcessingException { + return new FormBody.Builder().add(AUDIENCE_FIELD_NAME, String.format("https://%s-firebolt-v2.us.auth0.com/api/v2/", environment)) + .add(GRAND_TYPE_FIELD_NAME, GRAND_TYPE_FIELD_VALUE) + .add(CLIENT_ID_FIELD_NAME, clientId) + .add(CLIENT_SECRET_FIELD_NAME, clientSecret).build(); + } + + @Override + public String getUri() { + return String.format(AUTH_URL, environment); + } } diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequest.java b/src/main/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequest.java deleted file mode 100644 index 1ef2b31e9..000000000 --- a/src/main/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.firebolt.jdbc.client.authentication; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.firebolt.jdbc.client.FireboltObjectMapper; - -import lombok.AllArgsConstructor; -import okhttp3.MediaType; -import okhttp3.RequestBody; - -@AllArgsConstructor -public class UsernamePasswordAuthenticationRequest implements AuthenticationRequest { - private static final String AUTH_URL = "%s/auth/v1/login"; - private static final String USERNAME_FIELD_NAME = "username"; - private static final String PASSWORD_FIELD_NAME = "password"; - private String username; - private String password; - private String host; - - public RequestBody getRequestBody() throws JsonProcessingException { - Map<String, Object> loginDetailsMap = new HashMap<>(); - loginDetailsMap.put(USERNAME_FIELD_NAME, username); - loginDetailsMap.put(PASSWORD_FIELD_NAME, password); - return RequestBody.create(FireboltObjectMapper.getInstance().writeValueAsString(loginDetailsMap), - MediaType.parse("application/json")); - } - - @Override - public String getUri() { - return String.format(AUTH_URL, host); - } - -} \ No newline at end of file diff --git a/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java b/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java new file mode 100644 index 000000000..e65850cbd --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java @@ -0,0 +1,38 @@ +package com.firebolt.jdbc.client.gateway; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.firebolt.jdbc.client.FireboltClient; +import com.firebolt.jdbc.connection.FireboltConnection; +import com.firebolt.jdbc.exception.FireboltException; +import lombok.CustomLog; +import okhttp3.OkHttpClient; + +@CustomLog +public class FireboltGatewayUrlClient extends FireboltClient { + + private static final String URL = "https://api.dev.firebolt.io/web/v3/account/%s/engineUrl"; + + public FireboltGatewayUrlClient(OkHttpClient httpClient, ObjectMapper objectMapper, + FireboltConnection fireboltConnection, String customDrivers, String customClients) { + super(httpClient, fireboltConnection, customDrivers, customClients, objectMapper); + } + + /** + * Returns the gateway URL + * + * @param accessToken the access token + * @return the account + */ + public GatewayUrlResponse getGatewayUrl(String accessToken, String account) + throws FireboltException { + String url = String.format(URL, account); + try { + return getResource(url, accessToken, GatewayUrlResponse.class); + } catch (FireboltException e) { + throw e; + } catch (Exception e) { + throw new FireboltException(String.format("Failed to get gateway url for account %s", account), e); + } + } + +} diff --git a/src/main/java/com/firebolt/jdbc/client/gateway/GatewayUrlResponse.java b/src/main/java/com/firebolt/jdbc/client/gateway/GatewayUrlResponse.java new file mode 100644 index 000000000..986e7f614 --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/client/gateway/GatewayUrlResponse.java @@ -0,0 +1,15 @@ +package com.firebolt.jdbc.client.gateway; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Value; + + +@Value +@AllArgsConstructor +@Builder +public class GatewayUrlResponse { + @JsonProperty("engineUrl") + String engineUrl; +} diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index 8afa08e8a..7e6d8563f 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -36,6 +36,9 @@ public class StatementClientImpl extends FireboltClient implements StatementClie private static final String TAB_SEPARATED_WITH_NAMES_AND_TYPES_FORMAT = "TabSeparatedWithNamesAndTypes"; + private static final List<String> URI_QUERY_SEGMENTS = Arrays.asList("dynamic","query"); + + private final BiPredicate<Call, String> isCallWithId = (call, id) -> call.request().tag() instanceof String && StringUtils.equals((String) call.request().tag(), id); @@ -173,7 +176,7 @@ public boolean isStatementRunning(String statementId) { } private URI buildQueryUri(FireboltProperties fireboltProperties, Map<String, String> parameters) { - return buildURI(fireboltProperties, parameters, Collections.emptyList()); + return buildURI(fireboltProperties, parameters, PropertyUtil.isLocalDb(fireboltProperties) ? Collections.emptyList() : URI_QUERY_SEGMENTS); } private URI buildCancelUri(FireboltProperties fireboltProperties, String id) { diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 400b230e4..4493d3f54 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -1,8 +1,10 @@ package com.firebolt.jdbc.connection; +import static com.firebolt.jdbc.connection.settings.FireboltProperties.SYSTEM_ENGINE_NAME; import static java.sql.ResultSet.TYPE_FORWARD_ONLY; import java.io.IOException; +import java.net.URI; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -11,6 +13,9 @@ import java.util.*; import java.util.concurrent.Executor; +import com.firebolt.jdbc.client.gateway.FireboltGatewayUrlClient; +import com.firebolt.jdbc.service.FireboltEngineService; +import com.firebolt.jdbc.service.FireboltGatewayUrlService; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -20,7 +25,6 @@ import com.firebolt.jdbc.annotation.NotImplemented; import com.firebolt.jdbc.client.FireboltObjectMapper; import com.firebolt.jdbc.client.HttpClientConfig; -import com.firebolt.jdbc.client.account.FireboltAccountClient; import com.firebolt.jdbc.client.authentication.FireboltAuthenticationClient; import com.firebolt.jdbc.client.query.StatementClientImpl; import com.firebolt.jdbc.connection.settings.FireboltProperties; @@ -31,7 +35,6 @@ import com.firebolt.jdbc.metadata.FireboltDatabaseMetadata; import com.firebolt.jdbc.metadata.FireboltSystemEngineDatabaseMetadata; import com.firebolt.jdbc.service.FireboltAuthenticationService; -import com.firebolt.jdbc.service.FireboltEngineService; import com.firebolt.jdbc.service.FireboltStatementService; import com.firebolt.jdbc.statement.FireboltStatement; import com.firebolt.jdbc.statement.preparedstatement.FireboltPreparedStatement; @@ -44,47 +47,52 @@ public class FireboltConnection implements Connection { private final FireboltAuthenticationService fireboltAuthenticationService; - private final FireboltEngineService fireboltEngineService; private final FireboltStatementService fireboltStatementService; + + private final FireboltEngineService fireboltEngineService; + + private final FireboltGatewayUrlService fireboltGatewayUrlService; private final String httpConnectionUrl; private final List<FireboltStatement> statements; private final int connectionTimeout; private final boolean systemEngine; private boolean closed = true; private FireboltProperties sessionProperties; + private FireboltProperties internalSystemEngineProperties; private int networkTimeout; //Properties that are used at the beginning of the connection for authentication private final FireboltProperties loginProperties; public FireboltConnection(@NonNull String url, Properties connectionSettings, - FireboltAuthenticationService fireboltAuthenticationService, FireboltEngineService fireboltEngineService, - FireboltStatementService fireboltStatementService) throws FireboltException { + FireboltAuthenticationService fireboltAuthenticationService, + FireboltGatewayUrlService fireboltGatewayUrlService, FireboltStatementService fireboltStatementService, FireboltEngineService fireboltEngineService) throws FireboltException { this.fireboltAuthenticationService = fireboltAuthenticationService; - this.fireboltEngineService = fireboltEngineService; - loginProperties = this.extractFireboltProperties(url, connectionSettings); + this.fireboltGatewayUrlService = fireboltGatewayUrlService; + this.loginProperties = this.extractFireboltProperties(url, connectionSettings); this.httpConnectionUrl = getHttpConnectionUrl(loginProperties); this.fireboltStatementService = fireboltStatementService; this.statements = new ArrayList<>(); this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); this.systemEngine = loginProperties.isSystemEngine(); + this.fireboltEngineService = fireboltEngineService; this.connect(); } @ExcludeFromJacocoGeneratedReport public FireboltConnection(@NonNull String url, Properties connectionSettings) throws FireboltException { ObjectMapper objectMapper = FireboltObjectMapper.getInstance(); - loginProperties = this.extractFireboltProperties(url, connectionSettings); + this.loginProperties = this.extractFireboltProperties(url, connectionSettings); this.httpConnectionUrl = getHttpConnectionUrl(loginProperties); OkHttpClient httpClient = getHttpClient(loginProperties); this.systemEngine = loginProperties.isSystemEngine(); + this.fireboltGatewayUrlService = new FireboltGatewayUrlService(new FireboltGatewayUrlClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); this.fireboltAuthenticationService = new FireboltAuthenticationService( new FireboltAuthenticationClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); - this.fireboltEngineService = new FireboltEngineService( - new FireboltAccountClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); + this.fireboltEngineService = new FireboltEngineService(this); this.fireboltStatementService = new FireboltStatementService( - new StatementClientImpl(httpClient, this, objectMapper, loginProperties.getUserDrivers(), loginProperties.getUserClients()), systemEngine); + new StatementClientImpl(httpClient, this, objectMapper, loginProperties.getUserDrivers(), loginProperties.getUserClients())); this.statements = new ArrayList<>(); this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); @@ -103,17 +111,41 @@ private static OkHttpClient getHttpClient(FireboltProperties fireboltProperties) private void connect() throws FireboltException { String accessToken = this.getAccessToken(loginProperties).orElse(StringUtils.EMPTY); + closed = false; if (!PropertyUtil.isLocalDb(loginProperties)) { - String endpoint = fireboltEngineService.getEngine(httpConnectionUrl, loginProperties, accessToken) - .getEndpoint(); - this.sessionProperties = loginProperties.toBuilder().host(endpoint).build(); + internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, this.loginProperties.getAccount()); + if (!this.loginProperties.isSystemEngine()) { + sessionProperties = getSessionPropertiesForNonSystemEngine(); + } else { + //When using system engine, the system engine properties are the same as the session properties + sessionProperties = internalSystemEngineProperties.toBuilder().build(); + } } else { - this.sessionProperties = loginProperties; + //When running packdb locally, the login properties are the session properties + sessionProperties = loginProperties; } - closed = false; log.debug("Connection opened"); } + private FireboltProperties getSessionPropertiesForNonSystemEngine() { + +/* This is currently not supported + Engine engine = fireboltEngineService.getEngine(this.loginProperties.getEngine(), this.loginProperties.getDatabase()); + return loginProperties.toBuilder().host(engine.getEndpoint()).engine(engine.getName()).build();*/ + //temporary workaround + return internalSystemEngineProperties.toBuilder().build(); + } + + private FireboltProperties createInternalSystemEngineProperties(String accessToken, String account) throws FireboltException { + String systemEngineEndpoint = fireboltGatewayUrlService.getUrl(accessToken, account); + return this.loginProperties + .toBuilder() + .systemEngine(true) + .engine(SYSTEM_ENGINE_NAME) + .compress(false) + .host(URI.create(systemEngineEndpoint).getHost()).database(null).build(); + } + public void removeExpiredTokens() throws FireboltException { fireboltAuthenticationService.removeConnectionTokens(httpConnectionUrl, loginProperties); } @@ -147,6 +179,10 @@ public Statement createStatement(FireboltProperties fireboltProperties) throws S return fireboltStatement; } + public Statement createSystemEngineStatementStatement() throws SQLException { + return createStatement(internalSystemEngineProperties); + } + private void addStatement(FireboltStatement statement) throws SQLException { synchronized (statements) { this.validateConnectionIsNotClose(); @@ -194,9 +230,8 @@ public void setCatalog(String catalog) throws SQLException { // no-op as catalogs are not supported } - public String getEngine() throws SQLException { - this.validateConnectionIsNotClose(); - return fireboltEngineService.getEngineNameFromHost(this.getSessionProperties().getHost()); + public String getEngine() { + return this.getSessionProperties().getEngine(); } @Override @@ -266,7 +301,7 @@ public void close() { } private FireboltProperties extractFireboltProperties(String jdbcUri, Properties connectionProperties) { - Properties propertiesFromUrl = FireboltJdbcUrlUtil.extractProperties(jdbcUri); + Properties propertiesFromUrl = UrlUtil.extractProperties(jdbcUri); return FireboltProperties.of(propertiesFromUrl, connectionProperties); } diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtil.java b/src/main/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtil.java deleted file mode 100644 index b509c8d67..000000000 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtil.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.firebolt.jdbc.connection; - -import java.net.URI; -import java.util.Optional; -import java.util.Properties; - -import org.apache.commons.lang3.StringUtils; - -import com.firebolt.jdbc.connection.settings.FireboltSessionProperty; - -import lombok.CustomLog; -import lombok.experimental.UtilityClass; - -@CustomLog -@UtilityClass -public class FireboltJdbcUrlUtil { - - public static final String JDBC_PREFIX = "jdbc:"; - - public static Properties extractProperties(String jdbcUrl) { - URI uri = extractUriFromJdbcUrl(jdbcUrl); - return parseUriQueryPart(uri); - } - - private URI extractUriFromJdbcUrl(String jdbcConnectionString) { - String cleanURI = StringUtils.replace(jdbcConnectionString, JDBC_PREFIX, ""); - return URI.create(cleanURI); - } - - private static Properties parseUriQueryPart(URI uri) { - Properties uriProperties = new Properties(); - String query = uri.getQuery(); - if (StringUtils.isNotBlank(query)) { - String[] queryKeyValues = query.split("&"); - for (String keyValue : queryKeyValues) { - String[] keyValueTokens = keyValue.split("="); - if (keyValueTokens.length == 2) { - uriProperties.put(keyValueTokens[0], keyValueTokens[1]); - } else { - log.warn("Cannot parse key-pair: {}", keyValue); - } - } - } - Optional.ofNullable(uri.getPath()) - .ifPresent(path -> uriProperties.put(FireboltSessionProperty.PATH.getKey(), path)); - Optional.ofNullable(uri.getHost()) - .ifPresent(host -> uriProperties.put(FireboltSessionProperty.HOST.getKey(), host)); - Optional.of(uri.getPort()).filter(p -> !p.equals(-1)) - .ifPresent(port -> uriProperties.put(FireboltSessionProperty.PORT.getKey(), String.valueOf(port))); - return uriProperties; - } -} diff --git a/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java b/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java new file mode 100644 index 000000000..e666dc77d --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java @@ -0,0 +1,44 @@ +package com.firebolt.jdbc.connection; + +import com.firebolt.jdbc.connection.settings.FireboltSessionProperty; +import lombok.CustomLog; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; + +import java.net.URI; +import java.util.Optional; +import java.util.Properties; + +@CustomLog +@UtilityClass +public class UrlUtil { + + public static final String JDBC_PREFIX = "jdbc:firebolt:"; + + public static Properties extractProperties(String jdbcUrl) { + return parseUriQueryPart(jdbcUrl); + } + + + private static Properties parseUriQueryPart(String jdbcConnectionString) { + String cleanURI = StringUtils.replace(jdbcConnectionString, JDBC_PREFIX, ""); + URI uri = URI.create(cleanURI); + Properties uriProperties = new Properties(); + String query = uri.getQuery(); + if (StringUtils.isNotBlank(query)) { + String[] queryKeyValues = query.split("&"); + for (String keyValue : queryKeyValues) { + String[] keyValueTokens = keyValue.split("="); + if (keyValueTokens.length == 2) { + uriProperties.put(keyValueTokens[0], keyValueTokens[1]); + } else { + log.warn("Cannot parse key-pair: {}", keyValue); + } + } + } + Optional.ofNullable(uri.getPath()).map(p -> !StringUtils.isEmpty(p) ? StringUtils.removeEnd(p, "/") : p).ifPresent(path -> uriProperties.put(FireboltSessionProperty.PATH.getKey(), path)); + Optional.of(uri.getPort()).filter(p -> !p.equals(-1)) + .ifPresent(port -> uriProperties.put(FireboltSessionProperty.PORT.getKey(), String.valueOf(port))); + return uriProperties; + } +} diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index 29b762f69..7966e2a8a 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -18,10 +18,10 @@ @CustomLog public class FireboltProperties { - private static final Pattern DB_PATH_PATTERN = Pattern.compile("/([a-zA-Z0-9_*\\-]+)"); + public static final String SYSTEM_ENGINE_NAME = "system"; + private static final Pattern DB_PATH_PATTERN = Pattern.compile("([a-zA-Z0-9_*\\-]+)"); private static final int FIREBOLT_SSL_PROXY_PORT = 443; private static final int FIREBOLT_NO_SSL_PROXY_PORT = 9090; - private static final String SYSTEM_ENGINE_NAME = "system"; private static final Set<String> sessionPropertyKeys = Arrays.stream(FireboltSessionProperty.values()) .map(property -> { @@ -55,6 +55,8 @@ public class FireboltProperties { Integer tcpKeepInterval; boolean logResultSet; boolean systemEngine; + String organization; + String environment; String userDrivers; String userClients; @@ -69,7 +71,8 @@ public static FireboltProperties of(Properties... properties) { String user = getSetting(mergedProperties, FireboltSessionProperty.USER); String password = getSetting(mergedProperties, FireboltSessionProperty.PASSWORD); String path = getSetting(mergedProperties, FireboltSessionProperty.PATH); - String engine = getSetting(mergedProperties, FireboltSessionProperty.ENGINE); + String database = getDatabase(mergedProperties, path); + String engine = getEngine(mergedProperties, database); boolean isSystemEngine = isSystemEngine(engine); boolean compress = ((Boolean) getSetting(mergedProperties, FireboltSessionProperty.COMPRESS)) && !isSystemEngine; @@ -84,12 +87,13 @@ public static FireboltProperties of(Properties... properties) { int tcpKeepIdle = getSetting(mergedProperties, FireboltSessionProperty.TCP_KEEP_IDLE); int tcpKeepCount = getSetting(mergedProperties, FireboltSessionProperty.TCP_KEEP_COUNT); boolean logResultSet = getSetting(mergedProperties, FireboltSessionProperty.LOG_RESULT_SET); + String environment = getSetting(mergedProperties, FireboltSessionProperty.ENVIRONMENT); String driverVersions = getSetting(mergedProperties, FireboltSessionProperty.USER_DRIVERS); String clientVersions = getSetting(mergedProperties, FireboltSessionProperty.USER_CLIENTS); - String host = getHost(mergedProperties); + String host = getHost(environment, mergedProperties); Integer port = getPort(mergedProperties, ssl); - String database = getDatabase(mergedProperties, path); + Map<String, String> additionalProperties = getFireboltCustomProperties(mergedProperties); return FireboltProperties.builder().ssl(ssl).sslCertificatePath(sslRootCertificate).sslMode(sslMode).path(path) @@ -99,15 +103,25 @@ public static FireboltProperties of(Properties... properties) { .bufferSize(bufferSize).socketTimeoutMillis(socketTimeout).connectionTimeoutMillis(connectionTimeout) .tcpKeepInterval(tcpKeepInterval).tcpKeepCount(tcpKeepCount).tcpKeepIdle(tcpKeepIdle) .logResultSet(logResultSet).systemEngine(isSystemEngine) + .environment(environment) .userDrivers(driverVersions) .userClients(clientVersions) .build(); } - private static String getHost(Properties properties) { + private static String getEngine(Properties mergedProperties, String database) { + String engine = getSetting(mergedProperties, FireboltSessionProperty.ENGINE); + if (StringUtils.isEmpty(engine) && StringUtils.isEmpty(database)) { + return SYSTEM_ENGINE_NAME; + } else { + return engine; + } + } + + private static String getHost(String environment, Properties properties ) { String host = getSetting(properties, FireboltSessionProperty.HOST); if (StringUtils.isEmpty(host)) { - throw new IllegalArgumentException("Invalid host: The host is missing or empty"); + return String.format("api.%s.firebolt.io", environment); } else { return host; } @@ -125,8 +139,8 @@ private static Integer getPort(Properties properties, boolean ssl) { private static String getDatabase(Properties properties, String path) throws IllegalArgumentException { String database = getSetting(properties, FireboltSessionProperty.DATABASE); if (StringUtils.isEmpty(database)) { - if ("/".equals(path)) { - throw new IllegalArgumentException("A database must be provided"); + if ("/".equals(path) || StringUtils.isEmpty(path)) { + return null; } else { Matcher m = DB_PATH_PATTERN.matcher(path); if (m.matches()) { @@ -201,4 +215,9 @@ public void addProperty(@NonNull String key, String value) { public void addProperty(Pair<String, String> property) { this.addProperty(property.getLeft(), property.getRight()); } + + public static FireboltProperties toSystemEngineProperties(FireboltProperties properties) { + return properties.toBuilder().additionalProperties(new HashMap<>(properties.getAdditionalProperties())).build(); + } + } diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java index 162da19fb..5373bb9d0 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java @@ -10,7 +10,7 @@ @Getter public enum FireboltSessionProperty { - PATH("path", "/", String.class, "Path component of the URI"), + PATH("path", "", String.class, "Path component of the URI"), BUFFER_SIZE("buffer_size", 65536, Integer.class, "The buffer used to create the ResultSet in bytes"), SSL("ssl", true, Boolean.class, "Enable SSL/TLS for the connection"), SSL_CERTIFICATE_PATH("ssl_certificate_path", "", String.class, "SSL/TLS root certificate", "sslrootcert"), @@ -49,12 +49,12 @@ public enum FireboltSessionProperty { HOST("host", null, String.class, "Firebolt host - null by default"), PORT("port", null, Integer.class, "Firebolt port - null by default"), ENGINE("engine", null, String.class, "engine - null by default", "engine_name"), - ACCOUNT("account", null, String.class, "account - null by default"), + ACCOUNT("account", "firebolt", String.class, "account - null by default"), LOG_RESULT_SET("log_result_set", false, Boolean.class, "When set to true, the result of the queries executed are logged with the log level INFO. This has a negative performance impact and should be enabled only for debugging purposes"), USER_DRIVERS("user_drivers", null, String.class, "user drivers"), USER_CLIENTS("user_clients", null, String.class, "user clients"), - + ENVIRONMENT("environment", "app", String.class, "Firebolt environment", "env"), // We keep all the deprecated properties to ensure backward compatibility - but // they do not have any effect. @Deprecated diff --git a/src/main/java/com/firebolt/jdbc/metadata/MetadataUtil.java b/src/main/java/com/firebolt/jdbc/metadata/MetadataUtil.java index a56685f7a..31b16cfb6 100644 --- a/src/main/java/com/firebolt/jdbc/metadata/MetadataUtil.java +++ b/src/main/java/com/firebolt/jdbc/metadata/MetadataUtil.java @@ -1,13 +1,12 @@ package com.firebolt.jdbc.metadata; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; -import com.firebolt.jdbc.Query; +import org.apache.commons.lang3.StringUtils; +import lombok.Builder; import lombok.NonNull; +import lombok.Value; import lombok.experimental.UtilityClass; @UtilityClass @@ -102,4 +101,61 @@ public static String getDatabaseVersionQuery(String engine) { .conditions(Collections.singletonList(String.format("engine_name iLIKE '%s%%'", engine))).build() .toSql(); } + + /** + * Represents a SQL query that can be sent to Firebolt to receive metadata info + */ + @Builder + @Value + public static class Query { + String select; + String from; + String innerJoin; + String orderBy; + List<String> conditions; + + /** + * Parse the object to a SQL query that can be sent to Firebolt + * + * @return SQL query that can be sent to Firebolt + */ + public String toSql() { + StringBuilder query = new StringBuilder(); + if (StringUtils.isBlank(select)) { + throw new IllegalStateException("Cannot create query: SELECT cannot be blank"); + } + if (StringUtils.isBlank(from)) { + throw new IllegalStateException("Cannot create query: FROM cannot be blank"); + } + + query.append("SELECT ").append(select); + query.append(" FROM ").append(from); + if (StringUtils.isNotBlank(innerJoin)) { + query.append(" JOIN ").append(innerJoin); + } + query.append(getConditionsPart()); + if (StringUtils.isNotBlank(orderBy)) { + query.append(" order by ").append(orderBy); + } + return query.toString(); + } + + private String getConditionsPart() { + StringBuilder agg = new StringBuilder(); + Iterator<String> iter = conditions.iterator(); + if (iter.hasNext()) { + agg.append(" WHERE "); + } + if (iter.hasNext()) { + String entry = iter.next(); + agg.append(entry); + } + while (iter.hasNext()) { + String entry = iter.next(); + agg.append(" AND ").append(entry); + } + return agg.toString(); + } + } + } diff --git a/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSet.java b/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSet.java index 37edae5eb..8959aa2e4 100644 --- a/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSet.java +++ b/src/main/java/com/firebolt/jdbc/resultset/FireboltResultSet.java @@ -11,12 +11,12 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.firebolt.jdbc.QueryResult; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.text.StringEscapeUtils; import com.firebolt.jdbc.util.LoggerUtil; -import com.firebolt.jdbc.QueryResult; import com.firebolt.jdbc.annotation.ExcludeFromJacocoGeneratedReport; import com.firebolt.jdbc.annotation.NotImplemented; import com.firebolt.jdbc.exception.FireboltException; diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java b/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java index 9c393d287..1762fba39 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java @@ -41,7 +41,7 @@ public FireboltConnectionTokens getConnectionTokens(String host, FireboltPropert return foundToken; } else { FireboltConnectionTokens fireboltConnectionTokens = fireboltAuthenticationClient - .postConnectionTokens(host, loginProperties.getUser(), loginProperties.getPassword()); + .postConnectionTokens(host, loginProperties.getUser(), loginProperties.getPassword(), loginProperties.getEnvironment()); long durationInSeconds = getCachingDurationInSeconds( fireboltConnectionTokens.getExpiresInSeconds()); tokensMap.put(connectionParams, fireboltConnectionTokens, ExpirationPolicy.CREATED, diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java index 3ebac44b2..c7f59f3df 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java @@ -1,20 +1,16 @@ package com.firebolt.jdbc.service; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashSet; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.Optional; -import java.util.Set; + +import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; -import com.firebolt.jdbc.client.account.FireboltAccountClient; -import com.firebolt.jdbc.client.account.response.FireboltAccountResponse; -import com.firebolt.jdbc.client.account.response.FireboltDefaultDatabaseEngineResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineIdResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineResponse; import com.firebolt.jdbc.connection.Engine; -import com.firebolt.jdbc.connection.settings.FireboltProperties; +import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; import lombok.CustomLog; @@ -23,95 +19,78 @@ @RequiredArgsConstructor @CustomLog public class FireboltEngineService { - private static final Set<String> ENGINE_NOT_READY_STATUSES = new HashSet<>( - Arrays.asList("ENGINE_STATUS_PROVISIONING_STARTED", "ENGINE_STATUS_PROVISIONING_PENDING", - "ENGINE_STATUS_PROVISIONING_FINISHED", "ENGINE_STATUS_RUNNING_REVISION_STARTING")); - private static final String ERROR_NO_ENGINE_ATTACHED = "There is no Firebolt engine running on %s attached to the database %s. To connect first make sure there is a running engine and then try again."; - private static final String ERROR_NO_ENGINE_WITH_NAME = "There is no Firebolt engine running on %s with the name %s. To connect first make sure there is a running engine and then try again."; - private final FireboltAccountClient fireboltAccountClient; - - /** - * Returns the engine - * - * @param connectionUrl the connection url - * @param loginProperties properties to login - * @param accessToken the access token - * @return the engine - */ - public Engine getEngine(String connectionUrl, FireboltProperties loginProperties, String accessToken) - throws FireboltException { - String accountId = null; - Engine engine; - try { - if (StringUtils.isNotEmpty(loginProperties.getAccount())) { - accountId = getAccountId(connectionUrl, loginProperties.getAccount(), accessToken).orElse(null); - } - if (StringUtils.isEmpty(loginProperties.getEngine())) { - engine = getDefaultEngine(connectionUrl, accountId, loginProperties.getDatabase(), accessToken); - } else { - engine = getEngineWithName(connectionUrl, accountId, loginProperties.getEngine(), accessToken); - } - } catch (FireboltException e) { - throw e; - } catch (Exception e) { - throw new FireboltException("Failed to get engine", e); - } - validateEngineIsNotStarting(engine); - return engine; - } + private static final String ENGINE_URL = "engine_url"; + private static final String ENGINE_NAME = "engine_name"; + private static final String STATUS_FIELD_NAME = "status"; + private static final String DEFAULT_ENGINE_QUERY = "SELECT engs.engine_url, engs.status, engs.engine_name\n" + + "FROM information_schema.databases AS dbs\n" + + "INNER JOIN information_schema.engines AS engs\n" + + "ON engs.attached_to = dbs.database_name\n" + + "AND engs.engine_name = NULLIF(SPLIT_PART(ARRAY_FIRST(\n" + + " eng_name -> eng_name LIKE '%%(default)',\n" + + " SPLIT(',', attached_engines)\n" + + " ), ' ', 1), '')\n" + + "WHERE database_name = '%s'"; + private static final String ENGINE_QUERY = "SELECT engine_url, attached_to, status FROM information_schema.engines \n" + + "WHERE engine_name='%s'"; + private static final String RUNNING_STATUS = "running"; + private final FireboltConnection fireboltConnection; - private Engine getEngineWithName(String connectionUrl, String accountId, String engineName, String accessToken) - throws FireboltException, IOException { - FireboltEngineIdResponse response = fireboltAccountClient.getEngineId(connectionUrl, accountId, engineName, - accessToken); - String engineID = Optional.ofNullable(response).map(FireboltEngineIdResponse::getEngine) - .map(FireboltEngineIdResponse.Engine::getEngineId).orElseThrow(() -> new FireboltException( - "Failed to extract engine id field from the server response: the response from the server is invalid.")); - FireboltEngineResponse fireboltEngineResponse = fireboltAccountClient.getEngine(connectionUrl, accountId, - engineName, engineID, accessToken); + /** + * Extracts the engine name from host + * + * @param engineHost engine host + * @return the engine name + */ + public String getEngineNameByHost(String engineHost) throws FireboltException { + return Optional.ofNullable(engineHost).filter(host -> host.contains(".")).map(host -> host.split("\\.")[0]) + .map(host -> host.replace("-", "_")).orElseThrow(() -> new FireboltException( + String.format("Could not establish the engine from the host: %s", engineHost))); + } - return Optional.ofNullable(fireboltEngineResponse).map(FireboltEngineResponse::getEngine) - .filter(e -> StringUtils.isNotEmpty(e.getEndpoint())) - .map(e -> Engine.builder().endpoint(e.getEndpoint()).id(engineID).status(e.getCurrentStatus()) - .name(engineName).build()) - .orElseThrow(() -> new FireboltException( - String.format(ERROR_NO_ENGINE_WITH_NAME, connectionUrl, engineName))); - } + public Engine getEngine(@Nullable String name, @Nullable String database) throws FireboltException { + if (StringUtils.isEmpty(name)) { + return this.getDefaultEngine(database); + } else { + return Engine.builder().name(name).endpoint(getEngineEndpoint(name)).build(); + } + } - private Engine getDefaultEngine(String connectionUrl, String accountId, String database, String accessToken) - throws FireboltException, IOException { - FireboltDefaultDatabaseEngineResponse defaultEngine = fireboltAccountClient - .getDefaultEngineByDatabaseName(connectionUrl, accountId, database, accessToken); - return Optional.ofNullable(defaultEngine).map(FireboltDefaultDatabaseEngineResponse::getEngineUrl) - .map(url -> Engine.builder().endpoint(url).build()).orElseThrow( - () -> new FireboltException(String.format(ERROR_NO_ENGINE_ATTACHED, connectionUrl, database))); - } + private Engine getDefaultEngine(String database) throws FireboltException { + try (Statement statement = this.fireboltConnection.createSystemEngineStatementStatement(); + ResultSet resultSet = statement.executeQuery(String.format(DEFAULT_ENGINE_QUERY, database))) { + if (!resultSet.next()) { + throw new FireboltException(String.format("The default engine for the database %s could not be found", database)); + } + String status = resultSet.getString(STATUS_FIELD_NAME); + if (isEngineNotRunning(status)) { + throw new FireboltException(String.format("The default engine for the database %s is not running. Status: %s", database, status)); + } + return Engine.builder().endpoint(resultSet.getString(ENGINE_URL)).name(resultSet.getString(ENGINE_NAME)).build(); + } catch (SQLException sqlException) { + throw new FireboltException(String.format("Could not get default engine url for database %s", database), sqlException); + } + } - private Optional<String> getAccountId(String connectionUrl, String account, String accessToken) - throws FireboltException, IOException { - FireboltAccountResponse fireboltAccountResponse = fireboltAccountClient.getAccount(connectionUrl, account, - accessToken); - return Optional.ofNullable(fireboltAccountResponse).map(FireboltAccountResponse::getAccountId); - } + private String getEngineEndpoint(String engine) throws FireboltException { + try (Statement statement = this.fireboltConnection.createSystemEngineStatementStatement(); + ResultSet resultSet = statement.executeQuery(String.format(ENGINE_QUERY, engine))) { + if (!resultSet.next()) { + throw new FireboltException(String.format("The engine with the name %s could not be found", engine)); + } + String status = resultSet.getString(STATUS_FIELD_NAME); + if (isEngineNotRunning(status)) { + throw new FireboltException(String.format("The engine with the name %s is not running. Status: %s", engine, status)); + } + return resultSet.getString(ENGINE_URL); + } catch (SQLException sqlException) { + throw new FireboltException(String.format("Could not get engine url for engine %s", engine), sqlException); + } + } - private void validateEngineIsNotStarting(Engine engine) throws FireboltException { - if (StringUtils.isNotEmpty(engine.getId()) && StringUtils.isNotEmpty(engine.getStatus()) - && ENGINE_NOT_READY_STATUSES.contains(engine.getStatus())) { - throw new FireboltException(String.format( - "The engine %s is currently starting. Please wait until the engine is on and then execute the query again.", engine.getName())); - } - } + private boolean isEngineNotRunning(String status) { + return !StringUtils.equalsIgnoreCase(RUNNING_STATUS, status); + } - /** - * Extracts the engine name from host - * - * @param engineHost engine host - * @return the engine name - */ - public String getEngineNameFromHost(String engineHost) throws FireboltException { - return Optional.ofNullable(engineHost).filter(host -> host.contains(".")).map(host -> host.split("\\.")[0]) - .map(host -> host.replace("-", "_")).orElseThrow(() -> new FireboltException( - String.format("Could not establish the engine from the host: %s", engineHost))); - } } diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java b/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java new file mode 100644 index 000000000..0b91d0b87 --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java @@ -0,0 +1,15 @@ +package com.firebolt.jdbc.service; + +import com.firebolt.jdbc.client.gateway.FireboltGatewayUrlClient; +import com.firebolt.jdbc.exception.FireboltException; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class FireboltGatewayUrlService { + + private final FireboltGatewayUrlClient fireboltGatewayUrlClient; + + public String getUrl(String accessToken, String account) throws FireboltException { + return fireboltGatewayUrlClient.getGatewayUrl(accessToken, account).getEngineUrl(); + } +} diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltStatementService.java b/src/main/java/com/firebolt/jdbc/service/FireboltStatementService.java index 5a32aaebf..ad8e8acf4 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltStatementService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltStatementService.java @@ -28,7 +28,6 @@ public class FireboltStatementService { private static final String UNKNOWN_TABLE_NAME = "unknown"; private final StatementClient statementClient; - private final boolean systemEngine; /** * Executes statement @@ -38,12 +37,12 @@ public class FireboltStatementService { * @param queryTimeout query timeout * @param maxRows max rows * @param standardSql indicates if standard sql should be used + * @param systemEngine indicates if system engine is used * @param statement the statement * @return an InputStream with the result */ public Optional<ResultSet> execute(StatementInfoWrapper statementInfoWrapper, - FireboltProperties properties, int queryTimeout, int maxRows, boolean standardSql, - FireboltStatement statement) + FireboltProperties properties, int queryTimeout, int maxRows, boolean standardSql, boolean systemEngine, FireboltStatement statement) throws SQLException { InputStream is = statementClient.executeSqlStatement(statementInfoWrapper, properties, systemEngine, queryTimeout, maxRows, standardSql); @@ -60,7 +59,7 @@ public Optional<ResultSet> execute(StatementInfoWrapper statementInfoWrapper, public void abortStatement(@NonNull String statementId, @NonNull FireboltProperties properties) throws FireboltException { - if (systemEngine) { + if (properties.isSystemEngine()) { throw new FireboltException("Cannot cancel a statement using a system engine", INVALID_REQUEST); } else { statementClient.abortStatement(statementId, properties); diff --git a/src/main/java/com/firebolt/jdbc/statement/FireboltStatement.java b/src/main/java/com/firebolt/jdbc/statement/FireboltStatement.java index 73b4306cd..aa3c38dc8 100644 --- a/src/main/java/com/firebolt/jdbc/statement/FireboltStatement.java +++ b/src/main/java/com/firebolt/jdbc/statement/FireboltStatement.java @@ -109,7 +109,7 @@ private Optional<ResultSet> execute(StatementInfoWrapper statementInfoWrapper, b log.debug("The property from the query {} was stored", runningStatementId); } else { Optional<ResultSet> currentRs = statementService.execute(statementInfoWrapper, - this.sessionProperties, this.queryTimeout, this.maxRows, isStandardSql, this); + this.sessionProperties, this.queryTimeout, this.maxRows, isStandardSql, sessionProperties.isSystemEngine(), this); if (currentRs.isPresent()) { resultSet = currentRs.get(); currentUpdateCount = -1; // Always -1 when returning a ResultSet diff --git a/src/main/java/com/firebolt/jdbc/util/PropertyUtil.java b/src/main/java/com/firebolt/jdbc/util/PropertyUtil.java index 7b460cfb0..3be9bc77b 100644 --- a/src/main/java/com/firebolt/jdbc/util/PropertyUtil.java +++ b/src/main/java/com/firebolt/jdbc/util/PropertyUtil.java @@ -7,9 +7,9 @@ import java.util.Properties; import java.util.stream.Collectors; +import com.firebolt.jdbc.connection.UrlUtil; import org.apache.commons.lang3.StringUtils; -import com.firebolt.jdbc.connection.FireboltJdbcUrlUtil; import com.firebolt.jdbc.connection.settings.FireboltProperties; import com.firebolt.jdbc.connection.settings.FireboltSessionProperty; @@ -31,7 +31,7 @@ public class PropertyUtil { */ public DriverPropertyInfo[] getPropertyInfo(String url, Properties properties) { try { - Properties propertiesFromUrl = FireboltJdbcUrlUtil.extractProperties(url); + Properties propertiesFromUrl = UrlUtil.extractProperties(url); for (Object key : propertiesFromUrl.keySet()) { properties.put(key, propertiesFromUrl.get(key.toString())); } diff --git a/src/test/java/com/firebolt/jdbc/RawStatementTest.java b/src/test/java/com/firebolt/jdbc/RawStatementTest.java index 20e03ade9..197b23ace 100644 --- a/src/test/java/com/firebolt/jdbc/RawStatementTest.java +++ b/src/test/java/com/firebolt/jdbc/RawStatementTest.java @@ -6,6 +6,7 @@ import java.util.Arrays; import java.util.Collections; +import com.firebolt.jdbc.metadata.MetadataUtil.Query; import org.junit.jupiter.api.Test; class RawStatementTest { diff --git a/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java b/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java index 908d24d5c..eb0595b81 100644 --- a/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java +++ b/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD package com.firebolt.jdbc.client.account; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -204,3 +205,5 @@ private Map<String, String> extractHeadersMap(Request request) { return headers; } } +======= +>>>>>>> 5242315 (Start adding support for new identity) diff --git a/src/test/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactoryTest.java b/src/test/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactoryTest.java deleted file mode 100644 index 4a0aaebe2..000000000 --- a/src/test/java/com/firebolt/jdbc/client/authentication/AuthenticationRequestFactoryTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.firebolt.jdbc.client.authentication; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class AuthenticationRequestFactoryTest { - - @Test - void shouldGetServiceAccountRequestWhenUsernameDoesNotContainSpecialCharacter() { - String name = "265576ea-2478-4209-860c-f75f55e7c1f7"; - String password = "hello"; - AuthenticationRequest rq = AuthenticationRequestFactory.getAuthenticationRequest(name, password, "localhost"); - assertTrue(rq instanceof ServiceAccountAuthenticationRequest); - } - - @Test - void shouldGetUsernamePasswordRqWhenUsernameIsAnEmailAddress() { - String name = "tester@firebolt.io"; - String password = "hello"; - AuthenticationRequest rq = AuthenticationRequestFactory.getAuthenticationRequest(name, password, "localhost"); - assertTrue(rq instanceof UsernamePasswordAuthenticationRequest); - } - - @Test - void shouldGetUsernamePasswordRqWhenUsernameIsNullOrEmpty() { - assertTrue(AuthenticationRequestFactory.getAuthenticationRequest(null, null, - null) instanceof UsernamePasswordAuthenticationRequest); - assertTrue(AuthenticationRequestFactory.getAuthenticationRequest("", null, - null) instanceof UsernamePasswordAuthenticationRequest); - } -} \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClientTest.java b/src/test/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClientTest.java index 3e6c3e5a1..bc8f349f8 100644 --- a/src/test/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClientTest.java +++ b/src/test/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClientTest.java @@ -30,6 +30,8 @@ class FireboltAuthenticationClientTest { private static final String USER = "usr"; private static final String PASSWORD = "PA§§WORD"; + private static final String ENV = "ENV"; + @Spy private final ObjectMapper objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @@ -62,7 +64,7 @@ void shouldPostConnectionTokens() throws IOException, FireboltException { FireboltAuthenticationResponse.builder().accessToken("a").refreshToken("r").expiresIn(1).build()); when(body.string()).thenReturn(tokensResponse); - fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD); + fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV); verify(httpClient).newCall(requestArgumentCaptor.capture()); Request actualPost = requestArgumentCaptor.getValue(); @@ -82,7 +84,7 @@ void shouldThrowExceptionWhenStatusCodeIsNotFound() throws Exception { when(httpClient.newCall(any())).thenReturn(call); assertThrows(FireboltException.class, - () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD)); + () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV)); } @Test @@ -91,7 +93,7 @@ void shouldNotRetryWhenFacingANonRetryableException() throws Exception { when(call.execute()).thenThrow(IOException.class); when(httpClient.newCall(any())).thenReturn(call); - assertThrows(IOException.class, () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD)); + assertThrows(IOException.class, () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV)); verify(call).execute(); verify(call, times(0)).clone(); } @@ -107,6 +109,6 @@ void shouldThrowExceptionWhenStatusCodeIsForbidden() throws Exception { when(httpClient.newCall(any())).thenReturn(call); assertThrows(FireboltException.class, - () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD)); + () -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV)); } } diff --git a/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java b/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java index 0df3cf7e7..e2c887f5d 100644 --- a/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java +++ b/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java @@ -14,18 +14,12 @@ class ServiceAccountAuthenticationRequestTest { @Test void shouldCreateHttpEntityWithTheProvidedCredentials() throws IOException { ServiceAccountAuthenticationRequest serviceAccountAuthenticationHttpRequest = new ServiceAccountAuthenticationRequest( - "he-ll-o", "secret", "https://api.dev.firebolt.io:443"); + "he-ll-o", "secret", "dev"); RequestBody requestBody = serviceAccountAuthenticationHttpRequest.getRequestBody(); Buffer buffer = new Buffer(); requestBody.writeTo(buffer); - assertEquals("client_id=he-ll-o&client_secret=secret&grant_type=client_credentials", buffer.readUtf8()); + assertEquals("audience=https%3A%2F%2Fdev-firebolt-v2.us.auth0.com%2Fapi%2Fv2%2F&grant_type=client_credentials&client_id=he-ll-o&client_secret=secret", buffer.readUtf8()); } - @Test - void shouldGetUri() { - ServiceAccountAuthenticationRequest serviceAccountAuthenticationHttpRequest = new ServiceAccountAuthenticationRequest( - "he-ll-o", "secret", "https://api.dev.firebolt.io:443"); - assertEquals("https://api.dev.firebolt.io:443/auth/v1/token", serviceAccountAuthenticationHttpRequest.getUri()); - } } \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequestTest.java b/src/test/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequestTest.java deleted file mode 100644 index f16b0bb54..000000000 --- a/src/test/java/com/firebolt/jdbc/client/authentication/UsernamePasswordAuthenticationRequestTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.firebolt.jdbc.client.authentication; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import okhttp3.RequestBody; -import okio.Buffer; - -class UsernamePasswordAuthenticationRequestTest { - - @Test - void shouldCreateHttpEntityWithTheProvidedCredentials() throws IOException { - UsernamePasswordAuthenticationRequest usernamePasswordAuthenticationHttpRequest = new UsernamePasswordAuthenticationRequest( - "hello", "pa$$word", "https://api.dev.firebolt.io:443"); - RequestBody requestBody = usernamePasswordAuthenticationHttpRequest.getRequestBody(); - Buffer buffer = new Buffer(); - requestBody.writeTo(buffer); - // We transform the requests to map because the order of the fields is not - // guaranteed - Map<String, Object> expectedRequest = new ObjectMapper() - .readValue("{\"username\":\"hello\",\"password\":\"pa$$word\"}", HashMap.class); - Map<String, Object> actualRequest = new ObjectMapper().readValue(buffer.readUtf8(), HashMap.class); - assertEquals(expectedRequest, actualRequest); - } - - @Test - void getUri() { - UsernamePasswordAuthenticationRequest usernamePasswordAuthenticationHttpRequest = new UsernamePasswordAuthenticationRequest( - "hello", "pa$$word", "https://api.dev.firebolt.io:443"); - assertEquals("https://api.dev.firebolt.io:443/auth/v1/login", - usernamePasswordAuthenticationHttpRequest.getUri()); - } -} \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java new file mode 100644 index 000000000..27b666790 --- /dev/null +++ b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java @@ -0,0 +1,72 @@ +package com.firebolt.jdbc.client.gateway; + +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import static java.net.HttpURLConnection.HTTP_OK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import com.firebolt.jdbc.exception.ExceptionType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.firebolt.jdbc.connection.FireboltConnection; +import com.firebolt.jdbc.exception.FireboltException; + +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Response; +import okhttp3.ResponseBody; + +@ExtendWith(MockitoExtension.class) +class FireboltGatewayUrlClientTest { + + @Spy + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Mock + private OkHttpClient httpClient; + + @Mock + private FireboltConnection fireboltConnection; + + @InjectMocks + private FireboltGatewayUrlClient fireboltGatewayUrlClient; + + @Test + void shouldGetGatewayUrlWhenResponseIsOk() throws IOException, FireboltException { + GatewayUrlResponse response = GatewayUrlResponse.builder().engineUrl("http://engine").build(); + injectMockedResponse(httpClient, HTTP_OK, response); + assertEquals("http://engine", fireboltGatewayUrlClient.getGatewayUrl("access_token", "account").getEngineUrl()); + } + + @Test + void shouldThrowFireboltExceptionUponException() { + when(httpClient.newCall(any())).thenThrow(new IllegalArgumentException("ex")); + Exception ex = assertThrows(FireboltException.class, () ->fireboltGatewayUrlClient.getGatewayUrl("token", "acc")); + assertEquals("Failed to get gateway url for account acc", ex.getMessage()); + } + + private void injectMockedResponse(OkHttpClient httpClient, int code, GatewayUrlResponse gatewayUrlResponse) throws IOException { + Response response = mock(Response.class); + Call call = mock(Call.class); + when(httpClient.newCall(any())).thenReturn(call); + when(call.execute()).thenReturn(response); + ResponseBody body = mock(ResponseBody.class); + when(response.body()).thenReturn(body); + when(response.code()).thenReturn(code); + String gatewayResponse = new ObjectMapper() + .writeValueAsString(gatewayUrlResponse); + when(body.string()).thenReturn(gatewayResponse); + } +} \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index d360be097..bf4a7c0ad 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -8,11 +8,9 @@ import com.firebolt.jdbc.statement.StatementInfoWrapper; import com.firebolt.jdbc.statement.StatementUtil; import lombok.NonNull; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; +import okhttp3.*; import okio.Buffer; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -28,7 +26,6 @@ import java.util.Optional; import static com.firebolt.jdbc.client.UserAgentFormatter.userAgent; -import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -45,11 +42,14 @@ class StatementClientImplTest { private FireboltConnection connection; @Test + @Disabled("Disabled until engine_url is available") void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db1").compress(true) .host("firebolt1").port(555).build(); when(connection.getAccessToken()) .thenReturn(Optional.of("token")); + + injectMockedResponse(okHttpClient, 200); StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), "ConnA:1.0.9", "ConnB:2.0.9"); Call call = getMockedCallWithResponse(200); @@ -66,8 +66,8 @@ void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { assertEquals(expectedHeaders, extractHeadersMap(actualRequest)); assertEquals("show databases;", actualQuery); - assertEquals(format( - "http://firebolt1:555/?result_overflow_mode=break&database=db1&output_format=TabSeparatedWithNamesAndTypes&query_id=%s&compress=1&max_result_rows=1&max_execution_time=15", + assertEquals(String.format( + "http://firebolt1:555/dynamic/query?result_overflow_mode=break&database=db1&output_format=TabSeparatedWithNamesAndTypes&query_id=1cfd2cc6-3a62-48e2-ac9c-83846d70f16a&compress=1&max_result_rows=1&max_execution_time=15", statementInfoWrapper.getId()), actualRequest.url().toString()); } @@ -79,6 +79,7 @@ void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, .thenReturn(Optional.of("token")); StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), "ConnA:1.0.9", "ConnB:2.0.9"); + injectMockedResponse(okHttpClient, 200); Call call = getMockedCallWithResponse(200); when(okHttpClient.newCall(any())).thenReturn(call); StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("show databases").get(0); @@ -89,7 +90,7 @@ void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, String actualQuery = getActualRequestString(actualRequest); assertEquals("show databases;", actualQuery); - assertEquals("http://firebolt1:555/?output_format=TabSeparatedWithNamesAndTypes", + assertEquals("http://firebolt1:555/dynamic/query?output_format=TabSeparatedWithNamesAndTypes", actualRequest.url().toString()); } @@ -99,6 +100,7 @@ void shouldCancelSqlQuery() throws FireboltException, IOException { .host("firebolt1").port(555).build(); StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), "", ""); + injectMockedResponse(okHttpClient, 200); Call call = getMockedCallWithResponse(200); when(okHttpClient.newCall(any())).thenReturn(call); statementClient.abortStatement("12345", fireboltProperties); @@ -156,6 +158,17 @@ private Map<String, String> extractHeadersMap(Request request) { return headers; } + + private void injectMockedResponse(OkHttpClient httpClient, int code) throws IOException { + Response response = mock(Response.class); + Call call = mock(Call.class); + lenient().when(httpClient.newCall(any())).thenReturn(call); + lenient().when(call.execute()).thenReturn(response); + ResponseBody body = mock(ResponseBody.class); + lenient().when(response.body()).thenReturn(body); + lenient().when(response.code()).thenReturn(code); + } + @NonNull private String getActualRequestString(Request actualRequest) throws IOException { Buffer buffer = new Buffer(); diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index dbfd1dfd7..aa65e43a5 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -13,9 +13,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import com.firebolt.jdbc.service.FireboltEngineService; +import com.firebolt.jdbc.service.FireboltGatewayUrlService; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.*; @@ -25,15 +28,17 @@ import com.firebolt.jdbc.exception.ExceptionType; import com.firebolt.jdbc.exception.FireboltException; import com.firebolt.jdbc.service.FireboltAuthenticationService; -import com.firebolt.jdbc.service.FireboltEngineService; import com.firebolt.jdbc.service.FireboltStatementService; import com.firebolt.jdbc.statement.StatementInfoWrapper; @ExtendWith(MockitoExtension.class) class FireboltConnectionTest { - private static final String URL = "jdbc:firebolt://api.dev.firebolt.io/db"; - private static final String LOCAL_URL = "jdbc:firebolt://localhost:8123/local_dev_db?ssl=false&max_query_size=10000000&use_standard_sql=1&mask_internal_errors=0&firebolt_enable_beta_functions=1&firebolt_case_insensitive_identifiers=1&rest_api_pull_timeout_sec=3600&rest_api_pull_interval_millisec=5000&rest_api_retry_times=10"; + private static final String URL = "jdbc:firebolt:db?env=dev&engine=eng"; + + private static final String SYSTEM_ENGINE_URL = "jdbc:firebolt:db?env=dev&engine=system"; + + private static final String LOCAL_URL = "jdbc:firebolt:local_dev_db?ssl=false&max_query_size=10000000&use_standard_sql=1&mask_internal_errors=0&firebolt_enable_beta_functions=1&firebolt_case_insensitive_identifiers=1&rest_api_pull_timeout_sec=3600&rest_api_pull_interval_millisec=5000&rest_api_retry_times=10&host=localhost"; private final FireboltConnectionTokens fireboltConnectionTokens = FireboltConnectionTokens.builder().build(); @Captor private ArgumentCaptor<FireboltProperties> propertiesArgumentCaptor; @@ -41,6 +46,9 @@ class FireboltConnectionTest { private ArgumentCaptor<StatementInfoWrapper> queryInfoWrapperArgumentCaptor; @Mock private FireboltAuthenticationService fireboltAuthenticationService; + @Mock + private FireboltGatewayUrlService fireboltGatewayUrlService; + @Mock private FireboltEngineService fireboltEngineService; @Mock @@ -55,31 +63,31 @@ void init() throws FireboltException { connectionProperties.put("compress", "1"); lenient().when(fireboltAuthenticationService.getConnectionTokens(eq("https://api.dev.firebolt.io:443"), any())) .thenReturn(fireboltConnectionTokens); - lenient().when(fireboltEngineService.getEngine(any(), any(), any())).thenReturn(mock(Engine.class)); + lenient().when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("url"); } @Test void shouldInitConnection() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertFalse(fireboltConnection.isClosed()); } @Test void shouldNotFetchTokenNorEngineHostForLocalFirebolt() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(LOCAL_URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); verifyNoInteractions(fireboltAuthenticationService); - verifyNoInteractions(fireboltEngineService); + verifyNoInteractions(fireboltGatewayUrlService); assertFalse(fireboltConnection.isClosed()); } @Test void shouldPrepareStatement() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) .thenReturn(Optional.empty()); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); PreparedStatement statement = fireboltConnection .prepareStatement("INSERT INTO cars(sales, name) VALUES (?, ?)"); statement.setObject(1, 500); @@ -87,8 +95,7 @@ void shouldPrepareStatement() throws SQLException { statement.execute(); assertNotNull(fireboltConnection); assertNotNull(statement); - verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), any(), anyInt(), anyInt(), - anyBoolean(), any()); + verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any()); assertEquals("INSERT INTO cars(sales, name) VALUES (500, 'Ford')", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -96,7 +103,7 @@ void shouldPrepareStatement() throws SQLException { @Test void shouldCloseAllStatementsOnClose() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); Statement statement = fireboltConnection.createStatement(); Statement preparedStatement = fireboltConnection.prepareStatement("test"); fireboltConnection.close(); @@ -108,15 +115,15 @@ void shouldCloseAllStatementsOnClose() throws SQLException { @Test void shouldNotSetNewPropertyWhenConnectionIsNotValidWithTheNewProperty() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) .thenThrow(new FireboltException(ExceptionType.TOO_MANY_REQUESTS)); assertThrows(FireboltException.class, () -> fireboltConnection.addProperty(new ImmutablePair<>("custom_1", "1"))); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), any()); + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); assertNull(fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); @@ -124,17 +131,17 @@ void shouldNotSetNewPropertyWhenConnectionIsNotValidWithTheNewProperty() throws @Test void shouldSetNewPropertyWhenConnectionIsValidWithTheNewProperty() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) .thenReturn(Optional.empty()); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); Pair<String, String> newProperties = new ImmutablePair<>("custom_1", "1"); fireboltConnection.addProperty(newProperties); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), any()); + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); assertEquals("1", fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); @@ -142,23 +149,22 @@ void shouldSetNewPropertyWhenConnectionIsValidWithTheNewProperty() throws SQLExc @Test void shouldValidateConnectionWhenCallingIsValid() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); fireboltConnection.isValid(500); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), any()); + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @Test void shouldNotValidateConnectionWhenCallingIsValidWhenUsingSystemEngine() throws SQLException { Properties propertiesWithSystemEngine = new Properties(connectionProperties); - propertiesWithSystemEngine.put("engine_name", "system"); - FireboltConnection fireboltConnection = new FireboltConnection(URL, propertiesWithSystemEngine, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + FireboltConnection fireboltConnection = new FireboltConnection(SYSTEM_ENGINE_URL, propertiesWithSystemEngine, + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); fireboltConnection.isValid(500); verifyNoInteractions(fireboltStatementService); @@ -166,52 +172,52 @@ void shouldNotValidateConnectionWhenCallingIsValidWhenUsingSystemEngine() throws @Test void shouldIgnore429WhenValidatingConnection() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenThrow(new FireboltException(ExceptionType.TOO_MANY_REQUESTS)); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertTrue(fireboltConnection.isValid(500)); } @Test void shouldReturnFalseWhenValidatingConnectionThrowsAnException() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenThrow(new FireboltException(ExceptionType.ERROR)); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertFalse(fireboltConnection.isValid(500)); } @Test void shouldThrowExceptionWhenValidatingConnectionWithNegativeTimeout() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertThrows(FireboltException.class, () -> fireboltConnection.isValid(-1)); } @Test void shouldReturnFalseWhenValidatingClosedConnection() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); fireboltConnection.close(); assertFalse(fireboltConnection.isValid(50)); } @Test void shouldExtractConnectorOverrides() throws SQLException { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); connectionProperties.put("user_clients", "ConnA:1.0.9,ConnB:2.8.0"); connectionProperties.put("user_drivers", "DriverA:2.0.9,DriverB:3.8.0"); FireboltConnection fireboltConnectionImpl = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); PreparedStatement statement = fireboltConnectionImpl.prepareStatement("SELECT 1"); statement.execute(); verify(fireboltStatementService).execute(any(), propertiesArgumentCaptor.capture(), anyInt(), anyInt(), - anyBoolean(), any()); + anyBoolean(), anyBoolean(), any()); assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_clients")); assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_drivers")); assertNull(fireboltConnectionImpl.getSessionProperties().getAdditionalProperties().get("user_clients")); @@ -219,18 +225,19 @@ void shouldExtractConnectorOverrides() throws SQLException { } @Test + @Disabled("System engine is used until engine_url is available") void shouldGetEngineNameFromHost() throws SQLException { - when(fireboltEngineService.getEngineNameFromHost(any())).thenReturn("myHost_345"); + connectionProperties.put("engine", "hello"); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); - assertEquals("myHost_345", fireboltConnection.getEngine()); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); + assertEquals("hello", fireboltConnection.getEngine()); } @Test void shouldInitNetworkTimeoutWithPropertyByDefault() throws SQLException { connectionProperties.put("socket_timeout_millis", "60"); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertEquals(60, fireboltConnection.getNetworkTimeout()); } @@ -238,14 +245,14 @@ void shouldInitNetworkTimeoutWithPropertyByDefault() throws SQLException { void shouldInitConnectionTimeoutWithPropertyByDefault() throws SQLException { connectionProperties.put("connection_timeout_millis", "50"); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertEquals(50, fireboltConnection.getConnectionTimeout()); } @Test void shouldCloseConnectionWhenAbortingConnection() throws SQLException, InterruptedException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); ExecutorService executorService = Executors.newFixedThreadPool(10); fireboltConnection.abort(executorService); executorService.awaitTermination(1, TimeUnit.SECONDS); @@ -260,8 +267,10 @@ void shouldRemoveExpiredToken() throws SQLException { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); when(fireboltAuthenticationService.getConnectionTokens("http://host:8080", fireboltProperties)) .thenReturn(FireboltConnectionTokens.builder().build()); + lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://hello").build()); + FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); fireboltConnection.removeExpiredTokens(); verify(fireboltAuthenticationService).removeConnectionTokens("http://host:8080", fireboltProperties); } @@ -277,8 +286,9 @@ void shouldReturnConnectionTokenWhenAvailable() throws SQLException { FireboltConnectionTokens connectionTokens = FireboltConnectionTokens.builder().accessToken(accessToken).build(); when(fireboltAuthenticationService.getConnectionTokens(eq("http://host:8080"), any())) .thenReturn(connectionTokens); + lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://engineHost").build()); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); verify(fireboltAuthenticationService).getConnectionTokens("http://host:8080", fireboltProperties); assertEquals(accessToken, fireboltConnection.getAccessToken().get()); } @@ -291,7 +301,7 @@ void shouldNotReturnConnectionTokenWithLocalDb() throws SQLException { try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertEquals(Optional.empty(), fireboltConnection.getAccessToken()); verifyNoInteractions(fireboltAuthenticationService); } @@ -304,7 +314,7 @@ void shouldSetNetworkTimeout() throws SQLException { try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertEquals(5, fireboltConnection.getNetworkTimeout()); fireboltConnection.setNetworkTimeout(null, 1); assertEquals(1, fireboltConnection.getNetworkTimeout()); @@ -318,7 +328,7 @@ void shouldUseConnectionTimeoutFromProperties() throws SQLException { try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertEquals(20, fireboltConnection.getConnectionTimeout()); } } @@ -326,7 +336,7 @@ void shouldUseConnectionTimeoutFromProperties() throws SQLException { @Test void shouldThrowExceptionWhenTryingToUseClosedConnection() throws SQLException { FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltEngineService, fireboltStatementService); + fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); fireboltConnection.close(); assertThrows(FireboltException.class, fireboltConnection::getCatalog); } @@ -334,7 +344,7 @@ void shouldThrowExceptionWhenTryingToUseClosedConnection() throws SQLException { @Test void shouldUnwrapFireboltConnection() throws SQLException { Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltEngineService, fireboltStatementService); + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); assertTrue(connection.isWrapperFor(FireboltConnection.class)); assertEquals(connection, connection.unwrap(FireboltConnection.class)); } @@ -342,17 +352,18 @@ void shouldUnwrapFireboltConnection() throws SQLException { @Test void shouldThrowExceptionWhenCannotUnwrap() throws SQLException { try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltEngineService, fireboltStatementService)) { + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { assertFalse(connection.isWrapperFor(String.class)); assertThrows(SQLException.class, () -> connection.unwrap(String.class)); } } @Test + @Disabled("Db is currently not supported") void shouldGetDatabaseWhenGettingCatalog() throws SQLException { connectionProperties.put("database", "db"); try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltEngineService, fireboltStatementService)) { + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { assertEquals("db", connection.getCatalog()); } } @@ -361,7 +372,7 @@ void shouldGetDatabaseWhenGettingCatalog() throws SQLException { void shouldGetNoneTransactionIsolation() throws SQLException { connectionProperties.put("database", "db"); try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltEngineService, fireboltStatementService)) { + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { assertEquals(Connection.TRANSACTION_NONE, connection.getTransactionIsolation()); } } @@ -370,9 +381,47 @@ void shouldGetNoneTransactionIsolation() throws SQLException { void shouldThrowExceptionWhenPreparingStatementWIthInvalidResultSetType() throws SQLException { connectionProperties.put("database", "db"); try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltEngineService, fireboltStatementService)) { + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { assertThrows(SQLFeatureNotSupportedException.class, () -> connection.prepareStatement("any", TYPE_SCROLL_INSENSITIVE, 0)); } } + + @Test + @Disabled("Disabled until engine_url is available") + void shouldGetDefaultEngineWhenEngineIsNotProvided() throws SQLException { + connectionProperties.put("engine", ""); + when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().name("default_engine").endpoint("http://my-endpoint").build()); + try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + verify(fireboltEngineService).getEngine(null, "db"); + assertEquals("default_engine", connection.getSessionProperties().getEngine()); + assertEquals("my-endpoint", connection.getSessionProperties().getHost()); + } + } + + @Test + @Disabled("Disabled until engine_url is available") + void shouldGetEngineUrlWhenEngineIsProvided() throws SQLException { + connectionProperties.put("engine", "engine"); + when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("http://my_endpoint").build()); + try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + verify(fireboltEngineService).getEngine("engine", null); + assertEquals("http://my_endpoint", connection.getSessionProperties().getHost()); + } + } + + @Test + @Disabled("Db is currently not supported") + void shouldNotGetEngineUrlOrDefaultEngineUrlWhenUsingSystemEngine() throws SQLException { + connectionProperties.put("engine", "system"); + when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://my_endpoint"); + + try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, + fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + verify(fireboltEngineService, times(0)).getEngine(any(), any()); + assertEquals("http://my_endpoint", connection.getSessionProperties().getHost()); + } + } } diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtilTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtilTest.java deleted file mode 100644 index 598a6429c..000000000 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltJdbcUrlUtilTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.firebolt.jdbc.connection; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.util.Properties; - -import org.junit.jupiter.api.Test; - -class FireboltJdbcUrlUtilTest { - - @Test - void shouldGetAllPropertiesFromUri() { - String uri = "jdbc:firebolt://api.dev.firebolt.io:123/Tutorial_11_05?use_standard_sql=0&account=firebolt"; - Properties properties = FireboltJdbcUrlUtil.extractProperties(uri); - - Properties expectedProperties = new Properties(); - expectedProperties.put("path", "/Tutorial_11_05"); - expectedProperties.put("host", "api.dev.firebolt.io"); - expectedProperties.put("port", "123"); - expectedProperties.put("use_standard_sql", "0"); - expectedProperties.put("account", "firebolt"); - - assertEquals(expectedProperties, properties); - } -} diff --git a/src/test/java/com/firebolt/jdbc/connection/UrlUtilTest.java b/src/test/java/com/firebolt/jdbc/connection/UrlUtilTest.java new file mode 100644 index 000000000..d0e0ceacf --- /dev/null +++ b/src/test/java/com/firebolt/jdbc/connection/UrlUtilTest.java @@ -0,0 +1,29 @@ +package com.firebolt.jdbc.connection; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Properties; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class UrlUtilTest { + + + @ParameterizedTest + @CsvSource({ + "jdbc:firebolt:Tutorial_11_05/?use_standard_sql=0&host=api.dev.firebolt.io&account=firebolt, Tutorial_11_05, api.dev.firebolt.io, 0, firebolt", + "jdbc:firebolt:Tutorial_11_05?use_standard_sql=0&host=api.dev.firebolt.io&account=firebolt, Tutorial_11_05, api.dev.firebolt.io, 0, firebolt", + "jdbc:firebolt:/?use_standard_sql=0&host=api.dev.firebolt.io&account=firebolt, '', api.dev.firebolt.io, 0, firebolt" + }) + void shouldGetAllPropertiesFromUri(String uri, String expectedPath, String expectedHost, String expectedUseStandardSql, String expectedAccount) { + Properties properties = UrlUtil.extractProperties(uri); + Properties expectedProperties = new Properties(); + expectedProperties.put("path", expectedPath); + expectedProperties.put("host", expectedHost); + expectedProperties.put("use_standard_sql", expectedUseStandardSql); + expectedProperties.put("account", expectedAccount); + assertEquals(expectedProperties, properties); + } + +} diff --git a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java index 8edc77e92..a583c8939 100644 --- a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java @@ -13,12 +13,12 @@ class FireboltPropertiesTest { @Test void shouldHaveDefaultPropertiesWhenOnlyTheRequiredFieldsAreSpecified() { FireboltProperties expectedDefaultProperties = FireboltProperties.builder().database("db").bufferSize(65536) - .sslCertificatePath("").sslMode("strict").path("/").port(443) // 443 by default as SSL is enabled by - // default - .compress(true).user(null).password(null).host("host").ssl(true).additionalProperties(new HashMap<>()) - .account(null).engine(null).keepAliveTimeoutMillis(300000).maxConnectionsTotal(300).maxRetries(3) - .socketTimeoutMillis(0).connectionTimeoutMillis(60000).tcpKeepInterval(30).tcpKeepIdle(60) - .tcpKeepCount(10).build(); + .sslCertificatePath("").sslMode("strict").path("").port(443) // 443 by default as SSL is enabled by + .systemEngine(false).compress(true) // default + .user(null).password(null).host("host").ssl(true).additionalProperties(new HashMap<>()) + .account(null).keepAliveTimeoutMillis(300000).maxConnectionsTotal(300).maxRetries(3) + .socketTimeoutMillis(0).connectionTimeoutMillis(60000).tcpKeepInterval(30).environment("app").tcpKeepIdle(60) + .tcpKeepCount(10).account("firebolt").build(); Properties properties = new Properties(); properties.put("host", "host"); @@ -36,7 +36,7 @@ void shouldHaveAllTheSpecifiedCustomProperties() { properties.put("database", "myDb"); properties.put("ssl_certificate_path", "root_cert"); properties.put("ssl_mode", "none"); - properties.put("path", "/example"); + properties.put("path", "example"); properties.put("someCustomProperties", "custom_value"); properties.put("compress", "1"); @@ -44,18 +44,18 @@ void shouldHaveAllTheSpecifiedCustomProperties() { customProperties.put("someCustomProperties", "custom_value"); FireboltProperties expectedDefaultProperties = FireboltProperties.builder().bufferSize(51) - .sslCertificatePath("root_cert").sslMode("none").path("/example").database("myDb").compress(true) - .port(443).user(null).password(null).host("myDummyHost").ssl(true) - .additionalProperties(customProperties).account(null).engine(null).keepAliveTimeoutMillis(300000) + .sslCertificatePath("root_cert").sslMode("none").path("example").database("myDb").compress(true) + .port(443).user(null).password(null).host("myDummyHost").ssl(true).systemEngine(false) + .additionalProperties(customProperties).account(null).keepAliveTimeoutMillis(300000) .maxConnectionsTotal(300).maxRetries(3).socketTimeoutMillis(20).connectionTimeoutMillis(60000) - .tcpKeepInterval(30).tcpKeepIdle(60).tcpKeepCount(10).build(); + .tcpKeepInterval(30).tcpKeepIdle(60).tcpKeepCount(10).environment("app").account("firebolt").build(); assertEquals(expectedDefaultProperties, FireboltProperties.of(properties)); } @Test void shouldUsePathParamAsDb() { Properties properties = new Properties(); - properties.put("path", "/example"); + properties.put("path", "example"); properties.put("host", "host"); assertEquals("example", FireboltProperties.of(properties).getDatabase()); @@ -64,7 +64,7 @@ void shouldUsePathParamAsDb() { @Test void shouldSupportBooleansForBooleanProperties() { Properties properties = new Properties(); - properties.put("path", "/example"); + properties.put("path", "example"); properties.put("host", "host"); properties.put("ssl", "true"); properties.put("compress", "false"); @@ -76,7 +76,7 @@ void shouldSupportBooleansForBooleanProperties() { @Test void shouldSupportIntForBooleanProperties() { Properties properties = new Properties(); - properties.put("path", "/example"); + properties.put("path", "example"); properties.put("host", "host"); properties.put("ssl", "2"); properties.put("compress", "0"); @@ -88,7 +88,7 @@ void shouldSupportIntForBooleanProperties() { @Test void shouldUseCustomPortWhenProvided() { Properties properties = new Properties(); - properties.put("path", "/example"); + properties.put("path", "example"); properties.put("host", "host"); properties.put("port", "999"); @@ -96,35 +96,46 @@ void shouldUseCustomPortWhenProvided() { } @Test - void shouldThrowExceptionWhenNoDbProvided() { + void shouldUseSystemEngineWhenNoDbOrEngineProvided() { Properties properties = new Properties(); - properties.put("host", "host"); - - assertThrows(IllegalArgumentException.class, () -> FireboltProperties.of(properties)); + FireboltProperties fireboltProperties = FireboltProperties.of(properties); + assertTrue(FireboltProperties.of(properties).isSystemEngine()); + assertEquals("system", fireboltProperties.getEngine()); + assertNull(fireboltProperties.getDatabase()); + assertFalse(fireboltProperties.isCompress()); } @Test - void shouldThrowExceptionWhenHostIsNotProvided() { + void shouldNotUseSystemEngineWhenDbAsPathIsProvided() { Properties properties = new Properties(); - assertThrows(IllegalArgumentException.class, () -> FireboltProperties.of(properties)); + properties.put("path", "example"); + FireboltProperties fireboltProperties = FireboltProperties.of(properties); + assertFalse(FireboltProperties.of(properties).isSystemEngine()); + assertNull(fireboltProperties.getEngine()); + assertEquals("example", fireboltProperties.getDatabase()); + assertTrue(fireboltProperties.isCompress()); } @Test - void shouldThrowExceptionWhenDbPathFormatIsInvalid() { + void shouldNotUseSystemEngineWhenDbAsQueryParamIsProvided() { Properties properties = new Properties(); - properties.put("path", ""); - properties.put("host", "host"); - - assertThrows(IllegalArgumentException.class, () -> FireboltProperties.of(properties)); + properties.put("database", "example"); + FireboltProperties fireboltProperties = FireboltProperties.of(properties); + assertFalse(FireboltProperties.of(properties).isSystemEngine()); + assertNull(fireboltProperties.getEngine()); + assertEquals("example", fireboltProperties.getDatabase()); + assertTrue(fireboltProperties.isCompress()); } @Test - void shouldNotReturnAliasAsCustomProperty() { + void shouldNotUseSystemEngineWhenEngineIsProvided() { Properties properties = new Properties(); - properties.put("path", ""); - properties.put("host", "host"); - - assertThrows(IllegalArgumentException.class, () -> FireboltProperties.of(properties)); + properties.put("engine", "example"); + FireboltProperties fireboltProperties = FireboltProperties.of(properties); + assertFalse(FireboltProperties.of(properties).isSystemEngine()); + assertNull(fireboltProperties.getDatabase()); + assertEquals("example", fireboltProperties.getEngine()); + assertTrue(fireboltProperties.isCompress()); } @Test @@ -140,4 +151,5 @@ void shouldSupportUserClientsAndDrivers() { assertEquals(clients, FireboltProperties.of(properties).getUserClients()); assertEquals(drivers, FireboltProperties.of(properties).getUserDrivers()); } + } diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java index e93bee9cb..2a8700549 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java @@ -26,7 +26,9 @@ class FireboltAuthenticationServiceTest { private static final String USER = "usr"; private static final String PASSWORD = "PA§§WORD"; - private static final FireboltProperties PROPERTIES = FireboltProperties.builder().user(USER).password(PASSWORD) + private static final String ENV = "ENV"; + + private static final FireboltProperties PROPERTIES = FireboltProperties.builder().user(USER).password(PASSWORD).environment(ENV) .compress(true).build(); @Mock @@ -44,10 +46,10 @@ void shouldGetConnectionToken() throws IOException, FireboltException { String randomHost = UUID.randomUUID().toString(); FireboltConnectionTokens tokens = FireboltConnectionTokens.builder().expiresInSeconds(52) .refreshToken("refresh").accessToken("access").build(); - when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD)).thenReturn(tokens); + when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV)).thenReturn(tokens); assertEquals(tokens, fireboltAuthenticationService.getConnectionTokens(randomHost, PROPERTIES)); - verify(fireboltAuthenticationClient).postConnectionTokens(randomHost, USER, PASSWORD); + verify(fireboltAuthenticationClient).postConnectionTokens(randomHost, USER, PASSWORD, ENV); } @Test @@ -55,17 +57,17 @@ void shouldCallClientOnlyOnceWhenServiceCalledTwiceForTheSameHost() throws IOExc String randomHost = UUID.randomUUID().toString(); FireboltConnectionTokens tokens = FireboltConnectionTokens.builder().expiresInSeconds(52) .refreshToken("refresh").accessToken("access").build(); - when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD)).thenReturn(tokens); + when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV)).thenReturn(tokens); fireboltAuthenticationService.getConnectionTokens(randomHost, PROPERTIES); assertEquals(tokens, fireboltAuthenticationService.getConnectionTokens(randomHost, PROPERTIES)); - verify(fireboltAuthenticationClient).postConnectionTokens(randomHost, USER, PASSWORD); + verify(fireboltAuthenticationClient).postConnectionTokens(randomHost, USER, PASSWORD, ENV); } @Test void shouldThrowExceptionWithServerResponseWhenAResponseIsAvailable() throws IOException, FireboltException { String randomHost = UUID.randomUUID().toString(); - Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD)) + Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV)) .thenThrow(new FireboltException("An error happened during authentication", 403, "INVALID PASSWORD")); FireboltException ex = assertThrows(FireboltException.class, @@ -78,7 +80,7 @@ void shouldThrowExceptionWithServerResponseWhenAResponseIsAvailable() throws IOE @Test void shouldThrowExceptionWithExceptionMessageWhenAResponseIsNotAvailable() throws IOException, FireboltException { String randomHost = UUID.randomUUID().toString(); - Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD)) + Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV)) .thenThrow(new NullPointerException("NULL!")); FireboltException ex = assertThrows(FireboltException.class, diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java index ec83e03d8..37dd345ba 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java @@ -2,197 +2,95 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import com.firebolt.jdbc.client.account.FireboltAccountClient; -import com.firebolt.jdbc.client.account.response.FireboltAccountResponse; -import com.firebolt.jdbc.client.account.response.FireboltDefaultDatabaseEngineResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineIdResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineResponse; -import com.firebolt.jdbc.connection.settings.FireboltProperties; +import com.firebolt.jdbc.connection.Engine; +import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; @ExtendWith(MockitoExtension.class) class FireboltEngineServiceTest { - private static final String HOST = "https://host"; - private static final String ACCOUNT_ID = "account_id"; - private static final String DB_NAME = "dbName"; - private static final String ENGINE_NAME = "engineName"; - private static final String ENGINE_ID = "engineId"; - private static final String ACCESS_TOKEN = "token"; - - @Mock - private FireboltAccountClient fireboltAccountClient; - @InjectMocks private FireboltEngineService fireboltEngineService; - @Test - void shouldGetDefaultDbEngineWhenEngineNameIsNullOrEmpty() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .compress(false).build(); - - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getDefaultEngineByDatabaseName(HOST, ACCOUNT_ID, DB_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltDefaultDatabaseEngineResponse.builder().engineUrl("URL").build()); - fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN); - - verify(fireboltAccountClient).getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN); - verify(fireboltAccountClient).getDefaultEngineByDatabaseName(HOST, ACCOUNT_ID, DB_NAME, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); - } - - @Test - void shouldGThrowExceptionWhenGettingDefaultEngineAndTheUrlReturnedFromTheServerIsNull() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .compress(false).build(); - - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getDefaultEngineByDatabaseName(HOST, ACCOUNT_ID, DB_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltDefaultDatabaseEngineResponse.builder().engineUrl(null).build()); - FireboltException exception = assertThrows(FireboltException.class, - () -> fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN)); - assertEquals( - "There is no Firebolt engine running on https://host attached to the database dbName. To connect first make sure there is a running engine and then try again.", - exception.getMessage()); - - verify(fireboltAccountClient).getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN); - verify(fireboltAccountClient).getDefaultEngineByDatabaseName(HOST, ACCOUNT_ID, DB_NAME, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); - } + @Mock + private FireboltConnection fireboltConnection; @Test - void shouldGetEngineWhenEngineNameIsPresent() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .engine(ENGINE_NAME).compress(false).build(); - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId(ENGINE_ID).build()).build()); - when(fireboltAccountClient.getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN)) - .thenReturn(FireboltEngineResponse.builder() - .engine(FireboltEngineResponse.Engine.builder().endpoint("ANY").build()).build()); - fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN); - - verify(fireboltAccountClient).getAccount(properties.getHost(), ACCOUNT_ID, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); + void shouldGetEngineNameFromEngineHost() throws SQLException { + assertEquals("myHost_345", fireboltEngineService.getEngineNameByHost("myHost-345.firebolt.io")); } @Test - void shouldNotGetAccountWhileGettingEngineIfAccountIdIsNotPresent() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).database(DB_NAME) - .engine(ENGINE_NAME).compress(false).build(); - when(fireboltAccountClient.getEngineId(HOST, null, ENGINE_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId(ENGINE_ID).build()).build()); - when(fireboltAccountClient.getEngine(HOST, null, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN)) - .thenReturn(FireboltEngineResponse.builder() - .engine(FireboltEngineResponse.Engine.builder().endpoint("ANY").build()).build()); - fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN); - - verify(fireboltAccountClient, times(0)).getAccount(any(), any(), any()); - verify(fireboltAccountClient).getEngineId(HOST, null, ENGINE_NAME, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngine(HOST, null, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); + void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromTheHost() { + assertThrows(FireboltException.class, () -> fireboltEngineService.getEngineNameByHost("myHost-345")); } @Test - void shouldThrowExceptionWhenEngineNameIsSpecifiedButUrlIsNotPresentInTheResponse() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .engine(ENGINE_NAME).compress(false).build(); - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId(ENGINE_ID).build()).build()); - when(fireboltAccountClient.getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN)) - .thenReturn(FireboltEngineResponse.builder() - .engine(FireboltEngineResponse.Engine.builder().endpoint(null).build()).build()); - FireboltException exception = assertThrows(FireboltException.class, - () -> fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN)); - assertEquals( - "There is no Firebolt engine running on https://host with the name engineName. To connect first make sure there is a running engine and then try again.", - exception.getMessage()); - - verify(fireboltAccountClient).getAccount(properties.getHost(), ACCOUNT_ID, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); + void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromNullHost() { + assertThrows(FireboltException.class, () -> fireboltEngineService.getEngineNameByHost(null)); } @Test - void shouldThrowExceptionWhenEngineNameIsSpecifiedButEngineIdIsNotPresentInTheServerResponse() throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .engine(ENGINE_NAME).compress(false).build(); - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId(null).build()).build()); - FireboltException exception = assertThrows(FireboltException.class, - () -> fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN)); - assertEquals( - "Failed to extract engine id field from the server response: the response from the server is invalid.", - exception.getMessage()); - verify(fireboltAccountClient).getAccount(properties.getHost(), ACCOUNT_ID, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); + void shouldGetDefaultEngineWhenEngineNameIsNotProvided() throws SQLException { + Statement statement = mock(Statement.class); + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("status")).thenReturn("running"); + when(resultSet.getString("engine_url")).thenReturn("https://url"); + when(resultSet.getString("engine_name")).thenReturn("hello-engine"); + when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); + when(statement.executeQuery(any())).thenReturn(resultSet); + assertEquals(Engine.builder().endpoint("https://url").name("hello-engine").build(), + fireboltEngineService.getEngine(null, "db")); } @Test - void shouldGetEngineNameFromEngineHost() throws SQLException { - assertEquals("myHost_345", fireboltEngineService.getEngineNameFromHost("myHost-345.firebolt.io")); + void shouldThrowExceptionWhenDefaultEngineNotRunning() throws SQLException { + Statement statement = mock(Statement.class); + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("status")).thenReturn("down"); + when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); + when(statement.executeQuery(any())).thenReturn(resultSet); + assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(null, "db")); } @Test - void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromTheHost() { - assertThrows(FireboltException.class, () -> fireboltEngineService.getEngineNameFromHost("myHost-345")); + void shouldGetEngineWhenEngineNameIsProvided() throws SQLException { + Statement statement = mock(Statement.class); + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("status")).thenReturn("running"); + when(resultSet.getString("engine_url")).thenReturn("https://url"); + when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); + when(statement.executeQuery(any())).thenReturn(resultSet); + assertEquals(Engine.builder().endpoint("https://url").name("some-engine").build(), + fireboltEngineService.getEngine("some-engine", "db")); } @Test - void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromNullHost() { - assertThrows(FireboltException.class, () -> fireboltEngineService.getEngineNameFromHost(null)); + void shouldThrowExceptionWhenEngineNotRunning() throws SQLException { + Statement statement = mock(Statement.class); + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("status")).thenReturn("down"); + when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); + when(statement.executeQuery(any())).thenReturn(resultSet); + assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine("some-engine", "db")); } - @ParameterizedTest - @ValueSource(strings = { "ENGINE_STATUS_PROVISIONING_STARTED", "ENGINE_STATUS_PROVISIONING_FINISHED", - "ENGINE_STATUS_PROVISIONING_PENDING" }) - void shouldThrowExceptionWhenEngineStatusIndicatesEngineIsStarting(String status) throws Exception { - FireboltProperties properties = FireboltProperties.builder().host(HOST).account(ACCOUNT_ID).database(DB_NAME) - .engine(ENGINE_NAME).compress(false).build(); - when(fireboltAccountClient.getAccount(properties.getHost(), properties.getAccount(), ACCESS_TOKEN)) - .thenReturn(FireboltAccountResponse.builder().accountId(ACCOUNT_ID).build()); - when(fireboltAccountClient.getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN)) - .thenReturn(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId(ENGINE_ID).build()).build()); - when(fireboltAccountClient.getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN)) - .thenReturn(FireboltEngineResponse.builder() - .engine(FireboltEngineResponse.Engine.builder().endpoint("ANY").currentStatus(status).build()) - .build()); - FireboltException exception = assertThrows(FireboltException.class, - () -> fireboltEngineService.getEngine(HOST, properties, ACCESS_TOKEN)); - assertEquals("The engine engineName is currently starting. Please wait until the engine is on and then execute the query again.", - exception.getMessage()); - - verify(fireboltAccountClient).getAccount(properties.getHost(), ACCOUNT_ID, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngineId(HOST, ACCOUNT_ID, ENGINE_NAME, ACCESS_TOKEN); - verify(fireboltAccountClient).getEngine(HOST, ACCOUNT_ID, ENGINE_NAME, ENGINE_ID, ACCESS_TOKEN); - verifyNoMoreInteractions(fireboltAccountClient); - } } diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltStatementServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltStatementServiceTest.java index 9500ec4ce..643436829 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltStatementServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltStatementServiceTest.java @@ -35,9 +35,9 @@ void shouldExecuteQueryAndCreateResultSet() throws SQLException { StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("SELECT 1").get(0); FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("firebolt1") .ssl(true).compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, false); + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); - fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, 10, -1, true, + fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, 10, -1, true, false, mock(FireboltStatement.class)); verify(statementClient).executeSqlStatement(statementInfoWrapper, fireboltProperties, false, 10, -1, true); Assertions.assertEquals(1, mocked.constructed().size()); @@ -50,8 +50,8 @@ void shouldExecuteQueryWithLocalHostFormatParameters() throws SQLException { StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("SELECT 1").get(0); FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("localhost") .ssl(true).compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, false); - fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, -1, 10, true, + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); + fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, -1, 10, true, false, mock(FireboltStatement.class)); Assertions.assertEquals(1, mocked.constructed().size()); verify(statementClient).executeSqlStatement(statementInfoWrapper, fireboltProperties, false, -1, 10, true); @@ -63,7 +63,7 @@ void shouldCancelQueryWithAllRequiredParams() throws FireboltException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("http://firebolt1") .ssl(true).compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, false); + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); fireboltStatementService.abortStatement("123", fireboltProperties); verify(statementClient).abortStatement("123", fireboltProperties); } @@ -71,9 +71,9 @@ void shouldCancelQueryWithAllRequiredParams() throws FireboltException { @Test void shouldThrowExceptionWhenTryingToCancelQueryWithASystemEngine() throws FireboltException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("http://firebolt1") - .ssl(true).compress(false).build(); + .ssl(true).compress(false).systemEngine(true).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, true); + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); assertThrows(FireboltException.class, () -> fireboltStatementService.abortStatement("123", fireboltProperties)); verifyNoInteractions(statementClient); } @@ -84,8 +84,8 @@ void shouldExecuteQueryWithParametersForSystemEngine() throws SQLException { StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("SELECT 1").get(0); FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("firebolt1") .ssl(true).compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, true); - fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, 10, 10, true, + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); + fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, 10, 10, true, true, mock(FireboltStatement.class)); Assertions.assertEquals(1, mocked.constructed().size()); verify(statementClient).executeSqlStatement(statementInfoWrapper, fireboltProperties, true, 10, 10, true); @@ -100,8 +100,8 @@ void shouldIncludeNonStandardSqlQueryParamForNonStandardSql() throws SQLExceptio FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("localhost") .ssl(true).compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, true); - fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, -1, 0, false, + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); + fireboltStatementService.execute(statementInfoWrapper, fireboltProperties, -1, 0, false, true, mock(FireboltStatement.class)); Assertions.assertEquals(1, mocked.constructed().size()); verify(statementClient).executeSqlStatement(statementInfoWrapper, fireboltProperties, true, -1, 0, false); @@ -115,9 +115,9 @@ void shouldBeEmptyWithNonQueryStatement() throws SQLException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db").host("localhost").ssl(true) .compress(false).build(); - FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient, false); + FireboltStatementService fireboltStatementService = new FireboltStatementService(statementClient); Assertions.assertEquals(Optional.empty(), fireboltStatementService.execute(statementInfoWrapper, - fireboltProperties, -1, 10, true, mock(FireboltStatement.class))); + fireboltProperties, -1, 10, true, false, mock(FireboltStatement.class))); verify(statementClient).executeSqlStatement(statementInfoWrapper, fireboltProperties, false, -1, 10, true); } diff --git a/src/test/java/com/firebolt/jdbc/statement/FireboltStatementTest.java b/src/test/java/com/firebolt/jdbc/statement/FireboltStatementTest.java index 325e4aa8f..a2232287b 100644 --- a/src/test/java/com/firebolt/jdbc/statement/FireboltStatementTest.java +++ b/src/test/java/com/firebolt/jdbc/statement/FireboltStatementTest.java @@ -76,9 +76,9 @@ void shouldCloseInputStreamOnClose() throws SQLException { FireboltStatement fireboltStatement = FireboltStatement.builder().statementService(fireboltStatementService) .sessionProperties(fireboltProperties).connection(connection).build(); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.of(rs)); fireboltStatement.executeQuery("show database"); fireboltStatement.close(); @@ -106,11 +106,11 @@ void shouldExecuteIfUpdateStatementWouldNotReturnAResultSet() throws SQLExceptio try (FireboltStatement fireboltStatement = FireboltStatement.builder() .statementService(fireboltStatementService).connection(fireboltConnection) .sessionProperties(fireboltProperties).build()) { - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); assertEquals(0, fireboltStatement.executeUpdate("INSERT INTO cars(sales, name) VALUES (500, 'Ford')")); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(fireboltProperties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(sales, name) VALUES (500, 'Ford')", queryInfoWrapperArgumentCaptor.getValue().getSql()); assertEquals(0, fireboltStatement.getUpdateCount()); @@ -128,7 +128,7 @@ void shouldCloseCurrentAndGetMoreResultsForMultiStatementQuery() throws SQLExcep FireboltStatement fireboltStatement = FireboltStatement.builder().statementService(fireboltStatementService) .sessionProperties(fireboltProperties).connection(connection).build(); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.of(rs)).thenReturn(Optional.of(rs2)); fireboltStatement.execute("SELECT 1; SELECT 2;"); assertEquals(rs, fireboltStatement.getResultSet()); @@ -150,7 +150,7 @@ void shouldCloseCurrentAndGetMoreResultWhenCallingGetMoreResultsWithCloseCurrent .build(); FireboltStatement fireboltStatement = FireboltStatement.builder().statementService(fireboltStatementService) .sessionProperties(fireboltProperties).connection(connection).build(); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.of(mock(FireboltResultSet.class))); fireboltStatement.execute("SELECT 1; SELECT 2;"); ResultSet resultSet = fireboltStatement.getResultSet(); @@ -165,7 +165,7 @@ void shouldKeepCurrentAndGetMoreResultWhenCallingGetMoreResultsWithKeepCurrentRe .build(); FireboltStatement fireboltStatement = FireboltStatement.builder().statementService(fireboltStatementService) .sessionProperties(fireboltProperties).connection(connection).build(); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.of(mock(ResultSet.class))); fireboltStatement.execute("SELECT 1; SELECT 2;"); @@ -185,7 +185,7 @@ void shouldCloseUnclosedAndGetMoreResultWhenCallingGetMoreResultsWithCloseAllRes FireboltStatement fireboltStatement = FireboltStatement.builder().statementService(fireboltStatementService) .sessionProperties(fireboltProperties).connection(connection).build(); - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.of(rs)).thenReturn(Optional.of(rs2)).thenReturn(Optional.of(rs3)); fireboltStatement.execute("SELECT 1; SELECT 2; SELECT 3;"); diff --git a/src/test/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatementTest.java b/src/test/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatementTest.java index 35aa73073..95da31bc7 100644 --- a/src/test/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatementTest.java +++ b/src/test/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatementTest.java @@ -42,7 +42,7 @@ class FireboltPreparedStatementTest { @BeforeEach void beforeEach() throws SQLException { lenient().when(properties.getBufferSize()).thenReturn(10); - lenient().when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), any())) + lenient().when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); } @@ -56,7 +56,7 @@ void shouldExecute() throws SQLException { statement.setObject(2, "Ford"); statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars (sales, make) VALUES (500,'Ford')", queryInfoWrapperArgumentCaptor.getValue().getSql()); @@ -76,7 +76,7 @@ void shouldExecuteBatch() throws SQLException { statement.addBatch(); statement.executeBatch(); verify(fireboltStatementService, times(2)).execute(queryInfoWrapperArgumentCaptor.capture(), - eq(this.properties), anyInt(), anyInt(), anyBoolean(), any()); + eq(this.properties), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars (sales, make) VALUES (150,'Ford')", queryInfoWrapperArgumentCaptor.getAllValues().get(0).getSql()); @@ -113,7 +113,7 @@ void shouldExecuteWithSpecialCharactersInQuery() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals(expectedSql, queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -149,7 +149,7 @@ void shouldSetNull() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars (sales, make) VALUES (\\N,\\N)", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -162,7 +162,7 @@ void shouldSetBoolean() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(available) VALUES (1)", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -186,7 +186,7 @@ void shouldSetInt() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); } @Test @@ -197,7 +197,7 @@ void shouldSetLong() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(price) VALUES (50)", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -209,7 +209,7 @@ void shouldSetFloat() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(price) VALUES (5.5)", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -222,7 +222,7 @@ void shouldSetDouble() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); } @Test @@ -233,7 +233,7 @@ void shouldSetBigDecimal() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(price) VALUES (555555555555.55555555)", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -247,7 +247,7 @@ void shouldSetDate() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(release_date) VALUES ('2019-07-31')", queryInfoWrapperArgumentCaptor.getValue().getSql()); } @@ -261,7 +261,7 @@ void shouldSetTimeStamp() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals("INSERT INTO cars(release_date) VALUES ('2019-07-31 12:15:13')", queryInfoWrapperArgumentCaptor.getValue().getSql()); @@ -286,7 +286,7 @@ void shouldSetAllObjects() throws SQLException { statement.execute(); verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), eq(this.properties), - anyInt(), anyInt(), anyBoolean(), any()); + anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); assertEquals( "INSERT INTO cars(timestamp, date, float, long, big_decimal, null, boolean, int) VALUES ('2019-07-31 12:15:13','2019-07-31',5.5,5,555555555555.55555555,\\N,1,5)", diff --git a/src/test/java/com/firebolt/jdbc/util/PropertyUtilTest.java b/src/test/java/com/firebolt/jdbc/util/PropertyUtilTest.java index bc2aadfa5..e64869d4b 100644 --- a/src/test/java/com/firebolt/jdbc/util/PropertyUtilTest.java +++ b/src/test/java/com/firebolt/jdbc/util/PropertyUtilTest.java @@ -1,5 +1,6 @@ package com.firebolt.jdbc.util; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mockStatic; @@ -23,14 +24,14 @@ void shouldGetPropertyInfo() { FireboltSessionProperty.BUFFER_SIZE); mocked.when(FireboltSessionProperty::getNonDeprecatedProperties).thenReturn(existingProperties); DriverPropertyInfo accountDriverInfo = createExpectedDriverInfo(FireboltSessionProperty.ACCOUNT.getKey(), - FireboltSessionProperty.ACCOUNT.getDescription(), null); + FireboltSessionProperty.ACCOUNT.getDescription(), "myAccount"); DriverPropertyInfo bufferDriverInfo = createExpectedDriverInfo(FireboltSessionProperty.BUFFER_SIZE.getKey(), FireboltSessionProperty.BUFFER_SIZE.getDescription(), "1"); DriverPropertyInfo[] expected = new DriverPropertyInfo[] { accountDriverInfo, bufferDriverInfo }; for (int i = 0; i < expected.length; i++) { assertTrue(new ReflectionEquals( - PropertyUtil.getPropertyInfo("jdbc:firebolt://api.dev.firebolt.io/Tutorial_11_04?buffer_size=1", + PropertyUtil.getPropertyInfo("jdbc:firebolt:Tutorial_11_04/?buffer_size=1&account=myAccount", new Properties())[i]).matches(expected[i])); } } From d7533e36a66f724dc89176c9341a8b345ad9d074 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Thu, 6 Jul 2023 18:31:57 +0300 Subject: [PATCH 02/15] fixed implementation of new identity --- .../java/integration/ConnectionInfo.java | 58 ++- .../java/integration/IntegrationTest.java | 14 +- .../java/com/firebolt/FireboltDriver.java | 8 +- .../jdbc/client/account/FireboltAccount.java | 40 ++ .../account/FireboltAccountRetriever.java | 31 ++ .../ServiceAccountAuthenticationRequest.java | 15 +- .../gateway/FireboltGatewayUrlClient.java | 38 -- .../client/query/StatementClientImpl.java | 76 ++-- .../jdbc/connection/FireboltConnection.java | 109 +++--- .../com/firebolt/jdbc/connection/UrlUtil.java | 17 + .../settings/FireboltProperties.java | 26 +- .../settings/FireboltQueryParameterKey.java | 9 +- .../settings/FireboltSessionProperty.java | 5 +- .../metadata/FireboltDatabaseMetadata.java | 2 +- .../service/FireboltAccountIdService.java | 17 + .../FireboltAuthenticationService.java | 61 ++- .../jdbc/service/FireboltEngineService.java | 83 ++--- .../service/FireboltGatewayUrlService.java | 7 +- .../account/FireboltAccountClientTest.java | 209 ----------- ...rviceAccountAuthenticationRequestTest.java | 5 +- .../gateway/FireboltGatewayUrlClientTest.java | 77 ++-- .../client/query/StatementClientImplTest.java | 4 +- .../connection/FireboltConnectionTest.java | 352 +++++++++--------- .../settings/FireboltPropertiesTest.java | 4 +- .../FireboltAuthenticationServiceTest.java | 3 +- .../service/FireboltEngineServiceTest.java | 53 ++- 26 files changed, 600 insertions(+), 723 deletions(-) create mode 100644 src/main/java/com/firebolt/jdbc/client/account/FireboltAccount.java create mode 100644 src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java delete mode 100644 src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java create mode 100644 src/main/java/com/firebolt/jdbc/service/FireboltAccountIdService.java delete mode 100644 src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java diff --git a/src/integrationTest/java/integration/ConnectionInfo.java b/src/integrationTest/java/integration/ConnectionInfo.java index f4ea57ded..d9d527eeb 100644 --- a/src/integrationTest/java/integration/ConnectionInfo.java +++ b/src/integrationTest/java/integration/ConnectionInfo.java @@ -1,31 +1,59 @@ package integration; -import lombok.Value; - import java.util.Optional; -@Value +import static java.lang.System.getProperty; + public class ConnectionInfo { - private static ConnectionInfo INSTANCE; - String password; - String user; - String env; - String database; - String account; + private static volatile ConnectionInfo INSTANCE; + private final String principal; + private final String secret; + private final String env; + private final String database; + private final String account; + private final String engine; private ConnectionInfo() { - password = Optional.ofNullable(System.getProperty("password")).map(p -> p.replace("\"", "")).orElse(null); - user = Optional.ofNullable(System.getProperty("user")).map(u -> u.replace("\"", "")).orElse(null); - env = System.getProperty("env"); - database = System.getProperty("db"); - account = System.getProperty("account"); + principal = Optional.ofNullable(getProperty("client_id", getProperty("user"))).map(u -> u.replace("\"", "")).orElse(null); + secret = Optional.ofNullable(getProperty("client_secret", getProperty("password"))).map(p -> p.replace("\"", "")).orElse(null); + env = getProperty("env"); + database = getProperty("db"); + account = getProperty("account"); + engine = getProperty("engine"); } public static ConnectionInfo getInstance() { if (INSTANCE == null) { - INSTANCE = new ConnectionInfo(); + synchronized (ConnectionInfo.class) { + if (INSTANCE == null) { + INSTANCE = new ConnectionInfo(); + } + } } return INSTANCE; } + public String getPrincipal() { + return principal; + } + + public String getSecret() { + return secret; + } + + public String getEnv() { + return env; + } + + public String getDatabase() { + return database; + } + + public String getAccount() { + return account; + } + + public String getEngine() { + return engine; + } } diff --git a/src/integrationTest/java/integration/IntegrationTest.java b/src/integrationTest/java/integration/IntegrationTest.java index 29c0a6ebb..a66eba9d4 100644 --- a/src/integrationTest/java/integration/IntegrationTest.java +++ b/src/integrationTest/java/integration/IntegrationTest.java @@ -26,16 +26,16 @@ protected Connection createLocalConnection(String queryParams) throws SQLExcepti return DriverManager.getConnection( JDBC_URL_PREFIX + integration.ConnectionInfo.getInstance().getDatabase() + queryParams + "&host=localhost" + getAccountParam(), - integration.ConnectionInfo.getInstance().getUser(), - integration.ConnectionInfo.getInstance().getPassword()); + integration.ConnectionInfo.getInstance().getPrincipal(), + integration.ConnectionInfo.getInstance().getSecret()); } protected Connection createConnection() throws SQLException { return DriverManager.getConnection( JDBC_URL_PREFIX + integration.ConnectionInfo.getInstance().getDatabase() + "?" + getEnvParam() + getAccountParam() , - integration.ConnectionInfo.getInstance().getUser(), - integration.ConnectionInfo.getInstance().getPassword()); + integration.ConnectionInfo.getInstance().getPrincipal(), + integration.ConnectionInfo.getInstance().getSecret()); } protected Connection createConnection(String engine) throws SQLException { @@ -43,8 +43,8 @@ protected Connection createConnection(String engine) throws SQLException { JDBC_URL_PREFIX + integration.ConnectionInfo.getInstance().getDatabase() + Optional.ofNullable(engine).map(e -> "?" + getEnvParam() +"&engine=" + e + getAccountParam() ).orElse("?" + getEnvParam() + getAccountParam()), - integration.ConnectionInfo.getInstance().getUser(), - integration.ConnectionInfo.getInstance().getPassword()); + integration.ConnectionInfo.getInstance().getPrincipal(), + integration.ConnectionInfo.getInstance().getSecret()); } protected void setParam(Connection connection, String name, String value) throws SQLException { @@ -55,7 +55,7 @@ protected void setParam(Connection connection, String name, String value) throws @SneakyThrows protected void executeStatementFromFile(String path) { - executeStatementFromFile(path, null); + executeStatementFromFile(path, integration.ConnectionInfo.getInstance().getEngine()); } @SneakyThrows diff --git a/src/main/java/com/firebolt/FireboltDriver.java b/src/main/java/com/firebolt/FireboltDriver.java index 849e59adb..15e248c1b 100644 --- a/src/main/java/com/firebolt/FireboltDriver.java +++ b/src/main/java/com/firebolt/FireboltDriver.java @@ -30,11 +30,7 @@ public class FireboltDriver implements Driver { @Override public Connection connect(String url, Properties connectionSettings) throws SQLException { - if (!acceptsURL(url)) { - return null; - } else { - return new FireboltConnection(url, connectionSettings); - } + return acceptsURL(url) ? new FireboltConnection(url, connectionSettings) : null; } @Override @@ -43,7 +39,7 @@ public boolean acceptsURL(String url) { } @Override - public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { return PropertyUtil.getPropertyInfo(url, info); } diff --git a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccount.java b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccount.java new file mode 100644 index 000000000..31a94c90c --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccount.java @@ -0,0 +1,40 @@ +package com.firebolt.jdbc.client.account; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +public class FireboltAccount { + @JsonProperty + private final String id; + @JsonProperty + private final String region; + + @JsonCreator + public FireboltAccount(@JsonProperty("id") String id, @JsonProperty("region") String region) { + this.id = id; + this.region = region; + } + + public String getId() { + return id; + } + + public String getRegion() { + return region; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FireboltAccount account = (FireboltAccount) o; + return Objects.equals(id, account.id) && Objects.equals(region, account.region); + } + + @Override + public int hashCode() { + return Objects.hash(id, region); + } +} diff --git a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java new file mode 100644 index 000000000..cc7936877 --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java @@ -0,0 +1,31 @@ +package com.firebolt.jdbc.client.account; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.firebolt.jdbc.client.FireboltClient; +import com.firebolt.jdbc.connection.FireboltConnection; +import com.firebolt.jdbc.exception.FireboltException; +import okhttp3.OkHttpClient; + +import java.io.IOException; + +import static java.lang.String.format; + +public class FireboltAccountRetriever<T> extends FireboltClient { + private static final String URL = "https://api.dev.firebolt.io/web/v3/account/%s/%s"; + private final String path; + private final Class<T> type; + + public FireboltAccountRetriever(OkHttpClient httpClient, FireboltConnection connection, String customDrivers, String customClients, ObjectMapper objectMapper, String path, Class<T> type) { + super(httpClient, connection, customDrivers, customClients, objectMapper); + this.path = path; + this.type = type; + } + + public T retrieve(String accessToken, String accountName) throws FireboltException { + try { + return getResource(format(URL, accountName, path), accessToken, type); + } catch (IOException e) { + throw new FireboltException(String.format("Failed to get %s url for account %s", path, accountName), e); + } + } +} diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java b/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java index 4819bbc6a..50ca3e569 100644 --- a/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java +++ b/src/main/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequest.java @@ -1,14 +1,12 @@ package com.firebolt.jdbc.client.authentication; -import com.fasterxml.jackson.core.JsonProcessingException; -import lombok.AllArgsConstructor; import okhttp3.FormBody; import okhttp3.RequestBody; -@AllArgsConstructor public class ServiceAccountAuthenticationRequest implements AuthenticationRequest { private static final String AUDIENCE_FIELD_NAME = "audience"; + private static final String AUDIENCE_FIELD_VALUE = "https://api.firebolt.io"; private static final String GRAND_TYPE_FIELD_NAME = "grant_type"; private static final String GRAND_TYPE_FIELD_VALUE = "client_credentials"; private static final String CLIENT_ID_FIELD_NAME = "client_id"; @@ -19,9 +17,16 @@ public class ServiceAccountAuthenticationRequest implements AuthenticationReques private final String clientSecret; private final String environment; + public ServiceAccountAuthenticationRequest(String clientId, String clientSecret, String environment) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.environment = environment; + } + @Override - public RequestBody getRequestBody() throws JsonProcessingException { - return new FormBody.Builder().add(AUDIENCE_FIELD_NAME, String.format("https://%s-firebolt-v2.us.auth0.com/api/v2/", environment)) + public RequestBody getRequestBody() { + return new FormBody.Builder() + .add(AUDIENCE_FIELD_NAME, AUDIENCE_FIELD_VALUE) .add(GRAND_TYPE_FIELD_NAME, GRAND_TYPE_FIELD_VALUE) .add(CLIENT_ID_FIELD_NAME, clientId) .add(CLIENT_SECRET_FIELD_NAME, clientSecret).build(); diff --git a/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java b/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java deleted file mode 100644 index e65850cbd..000000000 --- a/src/main/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClient.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.firebolt.jdbc.client.gateway; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.firebolt.jdbc.client.FireboltClient; -import com.firebolt.jdbc.connection.FireboltConnection; -import com.firebolt.jdbc.exception.FireboltException; -import lombok.CustomLog; -import okhttp3.OkHttpClient; - -@CustomLog -public class FireboltGatewayUrlClient extends FireboltClient { - - private static final String URL = "https://api.dev.firebolt.io/web/v3/account/%s/engineUrl"; - - public FireboltGatewayUrlClient(OkHttpClient httpClient, ObjectMapper objectMapper, - FireboltConnection fireboltConnection, String customDrivers, String customClients) { - super(httpClient, fireboltConnection, customDrivers, customClients, objectMapper); - } - - /** - * Returns the gateway URL - * - * @param accessToken the access token - * @return the account - */ - public GatewayUrlResponse getGatewayUrl(String accessToken, String account) - throws FireboltException { - String url = String.format(URL, account); - try { - return getResource(url, accessToken, GatewayUrlResponse.class); - } catch (FireboltException e) { - throw e; - } catch (Exception e) { - throw new FireboltException(String.format("Failed to get gateway url for account %s", account), e); - } - } - -} diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index 7e6d8563f..b23464ca9 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -1,17 +1,5 @@ package com.firebolt.jdbc.client.query; -import static com.firebolt.jdbc.exception.ExceptionType.UNAUTHORIZED; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.*; -import java.util.function.BiPredicate; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; - import com.fasterxml.jackson.databind.ObjectMapper; import com.firebolt.jdbc.client.FireboltClient; import com.firebolt.jdbc.connection.FireboltConnection; @@ -24,12 +12,24 @@ import com.firebolt.jdbc.statement.rawstatement.RawStatement; import com.firebolt.jdbc.util.CloseableUtil; import com.firebolt.jdbc.util.PropertyUtil; -import com.google.common.collect.ImmutableMap; - import lombok.CustomLog; import lombok.NonNull; import okhttp3.*; import okhttp3.internal.http2.StreamResetException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.*; +import java.util.function.BiPredicate; +import java.util.function.Function; + +import static com.firebolt.jdbc.connection.settings.FireboltQueryParameterKey.DEFAULT_FORMAT; +import static com.firebolt.jdbc.connection.settings.FireboltQueryParameterKey.OUTPUT_FORMAT; +import static com.firebolt.jdbc.exception.ExceptionType.UNAUTHORIZED; +import static java.lang.String.format; @CustomLog public class StatementClientImpl extends FireboltClient implements StatementClient { @@ -68,12 +68,11 @@ public InputStream executeSqlStatement(@NonNull StatementInfoWrapper statementIn queryTimeout, maxRows); try { String uri = this.buildQueryUri(connectionProperties, params).toString(); - return executeSqlStatementWithRetryOnUnauthorized(statementInfoWrapper, connectionProperties, - formattedStatement, uri); + return executeSqlStatementWithRetryOnUnauthorized(statementInfoWrapper, connectionProperties, formattedStatement, uri); } catch (FireboltException e) { throw e; } catch (Exception e) { - String errorMessage = String.format("Error executing statement with id %s: %s", + String errorMessage = format("Error executing statement with id %s: %s", statementInfoWrapper.getId(), formattedStatement); if (e instanceof StreamResetException) { throw new FireboltException(errorMessage, e, ExceptionType.CANCELED); @@ -145,8 +144,7 @@ public void abortStatement(String id, FireboltProperties fireboltProperties) thr throw e; } } catch (Exception e) { - throw new FireboltException( - String.format("Could not cancel query: %s at %s", id, fireboltProperties.getHost()), e); + throw new FireboltException(format("Could not cancel query: %s at %s", id, fireboltProperties.getHost()), e); } } @@ -161,13 +159,15 @@ public void abortRunningHttpRequest(@NonNull String id) { } private Optional<Call> getQueuedCallWithId(String id) { - return getHttpClient().dispatcher().queuedCalls().stream().filter(call -> isCallWithId.test(call, id)) - .findAny(); + return getSelectedCallWithId(id, Dispatcher::queuedCalls); } private Optional<Call> getRunningCallWithId(String id) { - return getHttpClient().dispatcher().runningCalls().stream().filter(call -> isCallWithId.test(call, id)) - .findAny(); + return getSelectedCallWithId(id, Dispatcher::runningCalls); + } + + private Optional<Call> getSelectedCallWithId(String id, Function<Dispatcher, List<Call>> callsGetter) { + return callsGetter.apply(getHttpClient().dispatcher()).stream().filter(call -> isCallWithId.test(call, id)).findAny(); } @Override @@ -176,7 +176,7 @@ public boolean isStatementRunning(String statementId) { } private URI buildQueryUri(FireboltProperties fireboltProperties, Map<String, String> parameters) { - return buildURI(fireboltProperties, parameters, PropertyUtil.isLocalDb(fireboltProperties) ? Collections.emptyList() : URI_QUERY_SEGMENTS); + return buildURI(fireboltProperties, parameters, PropertyUtil.isLocalDb(fireboltProperties) || !fireboltProperties.isSystemEngine() ? Collections.emptyList() : URI_QUERY_SEGMENTS); } private URI buildCancelUri(FireboltProperties fireboltProperties, String id) { @@ -187,8 +187,9 @@ private URI buildCancelUri(FireboltProperties fireboltProperties, String id) { private URI buildURI(FireboltProperties fireboltProperties, Map<String, String> parameters, List<String> pathSegments) { HttpUrl.Builder httpUrlBuilder = new HttpUrl.Builder() - .scheme(Boolean.TRUE.equals(fireboltProperties.isSsl()) ? "https" : "http") - .host(fireboltProperties.getHost()).port(fireboltProperties.getPort()); + .scheme(fireboltProperties.isSsl() ? "https" : "http") + .host(fireboltProperties.getHost()) + .port(fireboltProperties.getPort()); parameters.forEach(httpUrlBuilder::addQueryParameter); pathSegments.forEach(httpUrlBuilder::addPathSegment); @@ -204,12 +205,13 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti getResponseFormatParameter(statementInfoWrapper.getType() == StatementType.QUERY, isLocalDb) .ifPresent(format -> params.put(format.getLeft(), format.getRight())); - // System engines do not support the following query params - if (!systemEngine) { + if (systemEngine) { + params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); // "01h11x0zmecanh1vp2q1mar5nh" + } else { + // System engines do not support the following query params params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); params.put(FireboltQueryParameterKey.QUERY_ID.getKey(), statementInfoWrapper.getId()); - params.put(FireboltQueryParameterKey.COMPRESS.getKey(), - String.format("%d", fireboltProperties.isCompress() ? 1 : 0)); + params.put(FireboltQueryParameterKey.COMPRESS.getKey(), fireboltProperties.isCompress() ? "1" : "0"); if (queryTimeout > -1) { params.put("max_execution_time", String.valueOf(queryTimeout)); @@ -224,20 +226,10 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti } private Optional<Pair<String, String>> getResponseFormatParameter(boolean isQuery, boolean isLocalDb) { - if (isQuery) { - if (isLocalDb) { - return Optional.of(new ImmutablePair<>(FireboltQueryParameterKey.DEFAULT_FORMAT.getKey(), - TAB_SEPARATED_WITH_NAMES_AND_TYPES_FORMAT)); - } else { - return Optional.of(new ImmutablePair<>(FireboltQueryParameterKey.OUTPUT_FORMAT.getKey(), - TAB_SEPARATED_WITH_NAMES_AND_TYPES_FORMAT)); - } - } - return Optional.empty(); + return isQuery ? Optional.of(Pair.of((isLocalDb ? DEFAULT_FORMAT : OUTPUT_FORMAT).getKey(), TAB_SEPARATED_WITH_NAMES_AND_TYPES_FORMAT)) : Optional.empty(); } private Map<String, String> getCancelParameters(String statementId) { - return ImmutableMap.of(FireboltQueryParameterKey.QUERY_ID.getKey(), statementId); + return Map.of(FireboltQueryParameterKey.QUERY_ID.getKey(), statementId); } - } diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 4493d3f54..26acdcd3d 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -1,31 +1,14 @@ package com.firebolt.jdbc.connection; -import static com.firebolt.jdbc.connection.settings.FireboltProperties.SYSTEM_ENGINE_NAME; -import static java.sql.ResultSet.TYPE_FORWARD_ONLY; - -import java.io.IOException; -import java.net.URI; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.sql.*; -import java.util.*; -import java.util.concurrent.Executor; - -import com.firebolt.jdbc.client.gateway.FireboltGatewayUrlClient; -import com.firebolt.jdbc.service.FireboltEngineService; -import com.firebolt.jdbc.service.FireboltGatewayUrlService; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; - import com.fasterxml.jackson.databind.ObjectMapper; -import com.firebolt.jdbc.util.PropertyUtil; import com.firebolt.jdbc.annotation.ExcludeFromJacocoGeneratedReport; import com.firebolt.jdbc.annotation.NotImplemented; import com.firebolt.jdbc.client.FireboltObjectMapper; import com.firebolt.jdbc.client.HttpClientConfig; +import com.firebolt.jdbc.client.account.FireboltAccount; +import com.firebolt.jdbc.client.account.FireboltAccountRetriever; import com.firebolt.jdbc.client.authentication.FireboltAuthenticationClient; +import com.firebolt.jdbc.client.gateway.GatewayUrlResponse; import com.firebolt.jdbc.client.query.StatementClientImpl; import com.firebolt.jdbc.connection.settings.FireboltProperties; import com.firebolt.jdbc.exception.ExceptionType; @@ -34,24 +17,33 @@ import com.firebolt.jdbc.exception.FireboltUnsupportedOperationException; import com.firebolt.jdbc.metadata.FireboltDatabaseMetadata; import com.firebolt.jdbc.metadata.FireboltSystemEngineDatabaseMetadata; -import com.firebolt.jdbc.service.FireboltAuthenticationService; -import com.firebolt.jdbc.service.FireboltStatementService; +import com.firebolt.jdbc.service.*; import com.firebolt.jdbc.statement.FireboltStatement; import com.firebolt.jdbc.statement.preparedstatement.FireboltPreparedStatement; - +import com.firebolt.jdbc.util.PropertyUtil; import lombok.CustomLog; import lombok.NonNull; import okhttp3.OkHttpClient; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.sql.*; +import java.util.*; +import java.util.concurrent.Executor; + +import static com.firebolt.jdbc.connection.settings.FireboltProperties.SYSTEM_ENGINE_NAME; +import static java.sql.ResultSet.TYPE_FORWARD_ONLY; @CustomLog public class FireboltConnection implements Connection { private final FireboltAuthenticationService fireboltAuthenticationService; private final FireboltStatementService fireboltStatementService; - private final FireboltEngineService fireboltEngineService; - private final FireboltGatewayUrlService fireboltGatewayUrlService; + private final FireboltAccountIdService fireboltAccountIdService; private final String httpConnectionUrl; private final List<FireboltStatement> statements; private final int connectionTimeout; @@ -65,46 +57,53 @@ public class FireboltConnection implements Connection { private final FireboltProperties loginProperties; public FireboltConnection(@NonNull String url, Properties connectionSettings, - FireboltAuthenticationService fireboltAuthenticationService, - FireboltGatewayUrlService fireboltGatewayUrlService, FireboltStatementService fireboltStatementService, FireboltEngineService fireboltEngineService) throws FireboltException { + FireboltAuthenticationService fireboltAuthenticationService, + FireboltGatewayUrlService fireboltGatewayUrlService, + FireboltStatementService fireboltStatementService, + FireboltEngineService fireboltEngineService, + FireboltAccountIdService fireboltAccountIdService) throws FireboltException { + this.loginProperties = this.extractFireboltProperties(url, connectionSettings); + this.fireboltAuthenticationService = fireboltAuthenticationService; this.fireboltGatewayUrlService = fireboltGatewayUrlService; - this.loginProperties = this.extractFireboltProperties(url, connectionSettings); this.httpConnectionUrl = getHttpConnectionUrl(loginProperties); this.fireboltStatementService = fireboltStatementService; + this.statements = new ArrayList<>(); this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); this.systemEngine = loginProperties.isSystemEngine(); this.fireboltEngineService = fireboltEngineService; + this.fireboltAccountIdService = fireboltAccountIdService; this.connect(); } + // This ugly code duplication between constructors is done because of back reference: dependent services require reference to current instance of FireboltConnection that prevents using constructor chaining or factory method. @ExcludeFromJacocoGeneratedReport public FireboltConnection(@NonNull String url, Properties connectionSettings) throws FireboltException { + this.loginProperties = extractFireboltProperties(url, connectionSettings); + OkHttpClient httpClient = getHttpClient(loginProperties); ObjectMapper objectMapper = FireboltObjectMapper.getInstance(); - this.loginProperties = this.extractFireboltProperties(url, connectionSettings); + + this.fireboltAuthenticationService = new FireboltAuthenticationService(new FireboltAuthenticationClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); + this.fireboltGatewayUrlService = new FireboltGatewayUrlService(new FireboltAccountRetriever<>(httpClient, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), objectMapper, "engineUrl", GatewayUrlResponse.class)); this.httpConnectionUrl = getHttpConnectionUrl(loginProperties); - OkHttpClient httpClient = getHttpClient(loginProperties); - this.systemEngine = loginProperties.isSystemEngine(); - this.fireboltGatewayUrlService = new FireboltGatewayUrlService(new FireboltGatewayUrlClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); - this.fireboltAuthenticationService = new FireboltAuthenticationService( - new FireboltAuthenticationClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); - this.fireboltEngineService = new FireboltEngineService(this); - this.fireboltStatementService = new FireboltStatementService( - new StatementClientImpl(httpClient, this, objectMapper, loginProperties.getUserDrivers(), loginProperties.getUserClients())); + this.fireboltStatementService = new FireboltStatementService(new StatementClientImpl(httpClient, this, objectMapper, loginProperties.getUserDrivers(), loginProperties.getUserClients())); + this.statements = new ArrayList<>(); this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); + this.systemEngine = loginProperties.isSystemEngine(); + this.fireboltEngineService = new FireboltEngineService(this); + this.fireboltAccountIdService = new FireboltAccountIdService(new FireboltAccountRetriever<>(httpClient, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), objectMapper, "resolve", FireboltAccount.class)); + this.connect(); } private static OkHttpClient getHttpClient(FireboltProperties fireboltProperties) throws FireboltException { try { - return HttpClientConfig.getInstance() == null ? HttpClientConfig.init(fireboltProperties) - : HttpClientConfig.getInstance(); - } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException - | IOException e) { + return HttpClientConfig.getInstance() == null ? HttpClientConfig.init(fireboltProperties) : HttpClientConfig.getInstance(); + } catch (GeneralSecurityException | IOException e) { throw new FireboltException("Could not instantiate http client", e); } } @@ -113,12 +112,18 @@ private void connect() throws FireboltException { String accessToken = this.getAccessToken(loginProperties).orElse(StringUtils.EMPTY); closed = false; if (!PropertyUtil.isLocalDb(loginProperties)) { - internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, this.loginProperties.getAccount()); - if (!this.loginProperties.isSystemEngine()) { + internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, loginProperties.getAccount()); + String accountId = fireboltAccountIdService.getValue(accessToken, loginProperties.getAccount()); + if (!loginProperties.isSystemEngine()) { + sessionProperties = internalSystemEngineProperties.toBuilder() + .engine(loginProperties.getEngine()) + .systemEngine(true) + .accountId(accountId) + .build(); sessionProperties = getSessionPropertiesForNonSystemEngine(); } else { //When using system engine, the system engine properties are the same as the session properties - sessionProperties = internalSystemEngineProperties.toBuilder().build(); + sessionProperties = internalSystemEngineProperties.toBuilder().accountId(accountId).build(); } } else { //When running packdb locally, the login properties are the session properties @@ -127,13 +132,9 @@ private void connect() throws FireboltException { log.debug("Connection opened"); } - private FireboltProperties getSessionPropertiesForNonSystemEngine() { - -/* This is currently not supported - Engine engine = fireboltEngineService.getEngine(this.loginProperties.getEngine(), this.loginProperties.getDatabase()); - return loginProperties.toBuilder().host(engine.getEndpoint()).engine(engine.getName()).build();*/ - //temporary workaround - return internalSystemEngineProperties.toBuilder().build(); + private FireboltProperties getSessionPropertiesForNonSystemEngine() throws FireboltException { + Engine engine = fireboltEngineService.getEngine(loginProperties.getEngine(), loginProperties.getDatabase()); + return loginProperties.toBuilder().host(engine.getEndpoint()).engine(engine.getName()).systemEngine(false).build(); } private FireboltProperties createInternalSystemEngineProperties(String accessToken, String account) throws FireboltException { @@ -143,7 +144,7 @@ private FireboltProperties createInternalSystemEngineProperties(String accessTok .systemEngine(true) .engine(SYSTEM_ENGINE_NAME) .compress(false) - .host(URI.create(systemEngineEndpoint).getHost()).database(null).build(); + .host(UrlUtil.createUrl(systemEngineEndpoint).getHost()).database(null).build(); } public void removeExpiredTokens() throws FireboltException { @@ -179,10 +180,6 @@ public Statement createStatement(FireboltProperties fireboltProperties) throws S return fireboltStatement; } - public Statement createSystemEngineStatementStatement() throws SQLException { - return createStatement(internalSystemEngineProperties); - } - private void addStatement(FireboltStatement statement) throws SQLException { synchronized (statements) { this.validateConnectionIsNotClose(); diff --git a/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java b/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java index e666dc77d..bc32fa51e 100644 --- a/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java +++ b/src/main/java/com/firebolt/jdbc/connection/UrlUtil.java @@ -5,7 +5,9 @@ import lombok.experimental.UtilityClass; import org.apache.commons.lang3.StringUtils; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import java.util.Optional; import java.util.Properties; @@ -41,4 +43,19 @@ private static Properties parseUriQueryPart(String jdbcConnectionString) { .ifPresent(port -> uriProperties.put(FireboltSessionProperty.PORT.getKey(), String.valueOf(port))); return uriProperties; } + + /** + * This factory method is similar to {@link URI#create(String)}. + * The difference is that `URI.host` of {@code http://something} is {@code null} while + * URL spec {@code http://something/} returns URI with host=something. + * @param spec – the String to parse as a URL. + * @return URL instance + */ + public static URL createUrl(String spec) { + try { + return new URL(spec); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } } diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index 7966e2a8a..8f523a117 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -1,17 +1,16 @@ package com.firebolt.jdbc.connection.settings; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; - import lombok.Builder; import lombok.CustomLog; import lombok.NonNull; import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; @Value @Builder(toBuilder = true) @@ -46,10 +45,11 @@ public class FireboltProperties { String sslCertificatePath; String sslMode; boolean compress; - String user; - String password; + String principal; + String secret; String engine; String account; + String accountId; Integer tcpKeepIdle; Integer tcpKeepCount; Integer tcpKeepInterval; @@ -68,8 +68,8 @@ public static FireboltProperties of(Properties... properties) { boolean ssl = getSetting(mergedProperties, FireboltSessionProperty.SSL); String sslRootCertificate = getSetting(mergedProperties, FireboltSessionProperty.SSL_CERTIFICATE_PATH); String sslMode = getSetting(mergedProperties, FireboltSessionProperty.SSL_MODE); - String user = getSetting(mergedProperties, FireboltSessionProperty.USER); - String password = getSetting(mergedProperties, FireboltSessionProperty.PASSWORD); + String principal = getSetting(mergedProperties, FireboltSessionProperty.CLIENT_ID); + String secret = getSetting(mergedProperties, FireboltSessionProperty.CLIENT_SECRET); String path = getSetting(mergedProperties, FireboltSessionProperty.PATH); String database = getDatabase(mergedProperties, path); String engine = getEngine(mergedProperties, database); @@ -97,7 +97,7 @@ public static FireboltProperties of(Properties... properties) { Map<String, String> additionalProperties = getFireboltCustomProperties(mergedProperties); return FireboltProperties.builder().ssl(ssl).sslCertificatePath(sslRootCertificate).sslMode(sslMode).path(path) - .port(port).database(database).compress(compress).user(user).password(password).host(host) + .port(port).database(database).compress(compress).principal(principal).secret(secret).host(host) .additionalProperties(additionalProperties).account(account).engine(engine) .keepAliveTimeoutMillis(keepAliveMillis).maxConnectionsTotal(maxTotal).maxRetries(maxRetries) .bufferSize(bufferSize).socketTimeoutMillis(socketTimeout).connectionTimeoutMillis(connectionTimeout) diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltQueryParameterKey.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltQueryParameterKey.java index ee2a183a3..41d503f98 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltQueryParameterKey.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltQueryParameterKey.java @@ -6,8 +6,13 @@ @RequiredArgsConstructor @Getter public enum FireboltQueryParameterKey { - DATABASE("database"), QUERY_ID("query_id"), COMPRESS("compress"), DEFAULT_FORMAT("default_format"), - OUTPUT_FORMAT("output_format"); + DATABASE("database"), + QUERY_ID("query_id"), + COMPRESS("compress"), + DEFAULT_FORMAT("default_format"), + OUTPUT_FORMAT("output_format"), + ACCOUNT_ID("account_id"), + ; private final String key; } \ No newline at end of file diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java index 5373bb9d0..3690f90f4 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java @@ -44,12 +44,13 @@ public enum FireboltSessionProperty { */ "compress", true, Boolean.class, "Whether to compress transferred data or not. Compressed by default"), DATABASE("database", null, String.class, "default database name"), - PASSWORD("password", null, String.class, "user password - null by default"), - USER("user", null, String.class, "user name - null by default"), + CLIENT_SECRET("client_secret", null, String.class, "user password - null by default", "password"), + CLIENT_ID("client_id", null, String.class, "user name - null by default", "user"), HOST("host", null, String.class, "Firebolt host - null by default"), PORT("port", null, Integer.class, "Firebolt port - null by default"), ENGINE("engine", null, String.class, "engine - null by default", "engine_name"), ACCOUNT("account", "firebolt", String.class, "account - null by default"), + ACCOUNT_ID("account_id", "firebolt", String.class, "accountId - null by default"), LOG_RESULT_SET("log_result_set", false, Boolean.class, "When set to true, the result of the queries executed are logged with the log level INFO. This has a negative performance impact and should be enabled only for debugging purposes"), USER_DRIVERS("user_drivers", null, String.class, "user drivers"), diff --git a/src/main/java/com/firebolt/jdbc/metadata/FireboltDatabaseMetadata.java b/src/main/java/com/firebolt/jdbc/metadata/FireboltDatabaseMetadata.java index 0e4b4c802..63d749479 100644 --- a/src/main/java/com/firebolt/jdbc/metadata/FireboltDatabaseMetadata.java +++ b/src/main/java/com/firebolt/jdbc/metadata/FireboltDatabaseMetadata.java @@ -352,7 +352,7 @@ public boolean allTablesAreSelectable() throws SQLException { @Override @ExcludeFromJacocoGeneratedReport public String getUserName() throws SQLException { - return connection.getSessionProperties().getUser(); + return connection.getSessionProperties().getPrincipal(); } @Override diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltAccountIdService.java b/src/main/java/com/firebolt/jdbc/service/FireboltAccountIdService.java new file mode 100644 index 000000000..f49c3d282 --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/service/FireboltAccountIdService.java @@ -0,0 +1,17 @@ +package com.firebolt.jdbc.service; + +import com.firebolt.jdbc.client.account.FireboltAccount; +import com.firebolt.jdbc.client.account.FireboltAccountRetriever; +import com.firebolt.jdbc.exception.FireboltException; + +public class FireboltAccountIdService { + private final FireboltAccountRetriever<FireboltAccount> firebolAccountClient; + + public FireboltAccountIdService(FireboltAccountRetriever<FireboltAccount> firebolAccountClient) { + this.firebolAccountClient = firebolAccountClient; + } + + public String getValue(String accessToken, String account) throws FireboltException { + return firebolAccountClient.retrieve(accessToken, account).getId(); + } +} diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java b/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java index 1762fba39..af697198e 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltAuthenticationService.java @@ -1,23 +1,22 @@ package com.firebolt.jdbc.service; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.StringUtils; - import com.firebolt.jdbc.client.authentication.FireboltAuthenticationClient; import com.firebolt.jdbc.connection.FireboltConnectionTokens; import com.firebolt.jdbc.connection.settings.FireboltProperties; import com.firebolt.jdbc.exception.FireboltException; - import lombok.CustomLog; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; -import net.jodah.expiringmap.ExpirationPolicy; import net.jodah.expiringmap.ExpiringMap; +import org.apache.commons.codec.binary.Hex; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; + +import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.SECONDS; +import static net.jodah.expiringmap.ExpirationPolicy.CREATED; @RequiredArgsConstructor @CustomLog @@ -32,36 +31,29 @@ public class FireboltAuthenticationService { public FireboltConnectionTokens getConnectionTokens(String host, FireboltProperties loginProperties) throws FireboltException { try { - ConnectParams connectionParams = new ConnectParams(host, loginProperties.getUser(), - loginProperties.getPassword()); + ConnectParams connectionParams = new ConnectParams(host, loginProperties.getPrincipal(), loginProperties.getSecret()); synchronized (this) { FireboltConnectionTokens foundToken = tokensMap.get(connectionParams); if (foundToken != null) { log.debug("Using the token of {} from the cache", host); return foundToken; - } else { - FireboltConnectionTokens fireboltConnectionTokens = fireboltAuthenticationClient - .postConnectionTokens(host, loginProperties.getUser(), loginProperties.getPassword(), loginProperties.getEnvironment()); - long durationInSeconds = getCachingDurationInSeconds( - fireboltConnectionTokens.getExpiresInSeconds()); - tokensMap.put(connectionParams, fireboltConnectionTokens, ExpirationPolicy.CREATED, - durationInSeconds, TimeUnit.SECONDS); - return fireboltConnectionTokens; } + FireboltConnectionTokens fireboltConnectionTokens = fireboltAuthenticationClient + .postConnectionTokens(host, loginProperties.getPrincipal(), loginProperties.getSecret(), loginProperties.getEnvironment()); + long durationInSeconds = getCachingDurationInSeconds(fireboltConnectionTokens.getExpiresInSeconds()); + tokensMap.put(connectionParams, fireboltConnectionTokens, CREATED, durationInSeconds, SECONDS); + return fireboltConnectionTokens; } } catch (Exception e) { log.error("Failed to connect to Firebolt", e); - if (e instanceof FireboltException - && StringUtils.isNotEmpty(((FireboltException) e).getErrorMessageFromServer())) { - throw new FireboltException(String.format( - "Failed to connect to Firebolt with the error from the server: %s, see logs for more info.", - ((FireboltException) e).getErrorMessageFromServer()), e); - } else { - throw new FireboltException( - String.format("Failed to connect to Firebolt with the error: %s, see logs for more info.", - e.getMessage()), - e); + String errorMessageTemplate = "Failed to connect to Firebolt with the error%s: %s, see logs for more info."; + if (e instanceof FireboltException) { + String server = ((FireboltException) e).getErrorMessageFromServer(); + if (server != null) { + throw new FireboltException(format(errorMessageTemplate, " from the server", server), e); + } } + throw new FireboltException(format(errorMessageTemplate, "", e.getMessage()), e); } } @@ -83,8 +75,7 @@ private long getCachingDurationInSeconds(long expireInSeconds) { public void removeConnectionTokens(String host, FireboltProperties loginProperties) throws FireboltException { try { log.debug("Removing connection token for host {}", host); - ConnectParams connectionParams = new ConnectParams(host, loginProperties.getUser(), - loginProperties.getPassword()); + ConnectParams connectionParams = new ConnectParams(host, loginProperties.getPrincipal(), loginProperties.getSecret()); tokensMap.remove(connectionParams); } catch (NoSuchAlgorithmException e) { throw new FireboltException("Could not remove connection tokens", e); @@ -96,11 +87,11 @@ private static class ConnectParams { public final String fireboltHost; public final String credentialsHash; - public ConnectParams(String fireboltHost, String user, String password) throws NoSuchAlgorithmException { + public ConnectParams(String fireboltHost, String principal, String secret) throws NoSuchAlgorithmException { this.fireboltHost = fireboltHost; MessageDigest sha256Instance = MessageDigest.getInstance("SHA-256"); - Optional.ofNullable(user).map(String::getBytes).ifPresent(sha256Instance::update); - Optional.ofNullable(password).map(String::getBytes).ifPresent(sha256Instance::update); + Optional.ofNullable(principal).map(String::getBytes).ifPresent(sha256Instance::update); + Optional.ofNullable(secret).map(String::getBytes).ifPresent(sha256Instance::update); this.credentialsHash = new String(Hex.encodeHex(sha256Instance.digest())); } } diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java index c7f59f3df..ed562548d 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java @@ -1,28 +1,28 @@ package com.firebolt.jdbc.service; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Optional; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; - +import com.firebolt.jdbc.CheckedFunction; import com.firebolt.jdbc.connection.Engine; import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; - import lombok.CustomLog; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import javax.annotation.Nullable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; + +import static java.lang.String.format; @RequiredArgsConstructor @CustomLog public class FireboltEngineService { - private static final String ENGINE_URL = "engine_url"; + private static final String ENGINE_URL = "url"; private static final String ENGINE_NAME = "engine_name"; private static final String STATUS_FIELD_NAME = "status"; - private static final String DEFAULT_ENGINE_QUERY = "SELECT engs.engine_url, engs.status, engs.engine_name\n" + + private static final String DEFAULT_ENGINE_QUERY = "SELECT engs.url, engs.status, engs.engine_name\n" + "FROM information_schema.databases AS dbs\n" + "INNER JOIN information_schema.engines AS engs\n" + "ON engs.attached_to = dbs.database_name\n" + @@ -30,9 +30,8 @@ public class FireboltEngineService { " eng_name -> eng_name LIKE '%%(default)',\n" + " SPLIT(',', attached_engines)\n" + " ), ' ', 1), '')\n" + - "WHERE database_name = '%s'"; - private static final String ENGINE_QUERY = "SELECT engine_url, attached_to, status FROM information_schema.engines \n" + - "WHERE engine_name='%s'"; + "WHERE database_name = ?"; + private static final String ENGINE_QUERY = "SELECT url, status FROM information_schema.engines WHERE engine_name=?"; private static final String RUNNING_STATUS = "running"; private final FireboltConnection fireboltConnection; @@ -45,7 +44,7 @@ public class FireboltEngineService { public String getEngineNameByHost(String engineHost) throws FireboltException { return Optional.ofNullable(engineHost).filter(host -> host.contains(".")).map(host -> host.split("\\.")[0]) .map(host -> host.replace("-", "_")).orElseThrow(() -> new FireboltException( - String.format("Could not establish the engine from the host: %s", engineHost))); + format("Could not establish the engine from the host: %s", engineHost))); } public Engine getEngine(@Nullable String name, @Nullable String database) throws FireboltException { @@ -57,40 +56,38 @@ public Engine getEngine(@Nullable String name, @Nullable String database) throws } private Engine getDefaultEngine(String database) throws FireboltException { - try (Statement statement = this.fireboltConnection.createSystemEngineStatementStatement(); - ResultSet resultSet = statement.executeQuery(String.format(DEFAULT_ENGINE_QUERY, database))) { - if (!resultSet.next()) { - throw new FireboltException(String.format("The default engine for the database %s could not be found", database)); - } - String status = resultSet.getString(STATUS_FIELD_NAME); - if (isEngineNotRunning(status)) { - throw new FireboltException(String.format("The default engine for the database %s is not running. Status: %s", database, status)); - } - return Engine.builder().endpoint(resultSet.getString(ENGINE_URL)).name(resultSet.getString(ENGINE_NAME)).build(); - } catch (SQLException sqlException) { - throw new FireboltException(String.format("Could not get default engine url for database %s", database), sqlException); - } + return getRowValue(DEFAULT_ENGINE_QUERY, database, rs -> Engine.builder().endpoint(rs.getString(ENGINE_URL)).name(rs.getString(ENGINE_NAME)).build(), + "The default engine for the database %s could not be found", + "The default engine for the database %s is not running. Status: %s", + "Could not get default engine url for database %s"); } private String getEngineEndpoint(String engine) throws FireboltException { - try (Statement statement = this.fireboltConnection.createSystemEngineStatementStatement(); - ResultSet resultSet = statement.executeQuery(String.format(ENGINE_QUERY, engine))) { - if (!resultSet.next()) { - throw new FireboltException(String.format("The engine with the name %s could not be found", engine)); - } - String status = resultSet.getString(STATUS_FIELD_NAME); - if (isEngineNotRunning(status)) { - throw new FireboltException(String.format("The engine with the name %s is not running. Status: %s", engine, status)); + return getRowValue(ENGINE_QUERY, engine, rs -> rs.getString(ENGINE_URL), + "The engine with the name %s could not be found", + "The engine with the name %s is not running. Status: %s", + "Could not get engine url for engine %s"); + } + + private <T> T getRowValue(String query, String queryArg, CheckedFunction<ResultSet, T> valueExtractor, String noDataMessage, String engineNotRunningMessage, String sqlFailureMessage) throws FireboltException { + try (PreparedStatement ps = fireboltConnection.prepareStatement(query)) { + ps.setString(1, queryArg); + try (ResultSet rs = ps.executeQuery()) { + if (!rs.next()) { + throw new FireboltException(format(noDataMessage, queryArg)); + } + String status = rs.getString(STATUS_FIELD_NAME); + if (isEngineNotRunning(status)) { + throw new FireboltException(format(engineNotRunningMessage, queryArg, status)); + } + return valueExtractor.apply(rs); } - return resultSet.getString(ENGINE_URL); - } catch (SQLException sqlException) { - throw new FireboltException(String.format("Could not get engine url for engine %s", engine), sqlException); + } catch (SQLException e) { + throw new FireboltException(format(sqlFailureMessage, queryArg), e); } } private boolean isEngineNotRunning(String status) { - return !StringUtils.equalsIgnoreCase(RUNNING_STATUS, status); + return !RUNNING_STATUS.equalsIgnoreCase(status); } - - } diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java b/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java index 0b91d0b87..f3df43fe4 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltGatewayUrlService.java @@ -1,15 +1,16 @@ package com.firebolt.jdbc.service; -import com.firebolt.jdbc.client.gateway.FireboltGatewayUrlClient; +import com.firebolt.jdbc.client.account.FireboltAccountRetriever; +import com.firebolt.jdbc.client.gateway.GatewayUrlResponse; import com.firebolt.jdbc.exception.FireboltException; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class FireboltGatewayUrlService { - private final FireboltGatewayUrlClient fireboltGatewayUrlClient; + private final FireboltAccountRetriever<GatewayUrlResponse> fireboltGatewayUrlClient; public String getUrl(String accessToken, String account) throws FireboltException { - return fireboltGatewayUrlClient.getGatewayUrl(accessToken, account).getEngineUrl(); + return fireboltGatewayUrlClient.retrieve(accessToken, account).getEngineUrl(); } } diff --git a/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java b/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java deleted file mode 100644 index eb0595b81..000000000 --- a/src/test/java/com/firebolt/jdbc/client/account/FireboltAccountClientTest.java +++ /dev/null @@ -1,209 +0,0 @@ -<<<<<<< HEAD -package com.firebolt.jdbc.client.account; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.firebolt.jdbc.client.account.response.FireboltAccountResponse; -import com.firebolt.jdbc.client.account.response.FireboltDefaultDatabaseEngineResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineIdResponse; -import com.firebolt.jdbc.client.account.response.FireboltEngineResponse; -import com.firebolt.jdbc.connection.FireboltConnection; -import com.firebolt.jdbc.exception.FireboltException; -import com.google.common.collect.ImmutableMap; -import okhttp3.*; -import org.junit.function.ThrowingRunnable; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.HashMap; -import java.util.Map; - -import static com.firebolt.jdbc.client.UserAgentFormatter.userAgent; -import static java.lang.String.format; -import static java.net.HttpURLConnection.*; -import static org.junit.Assert.assertThrows; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class FireboltAccountClientTest { - private static final String ACCESS_TOKEN = "token"; - private static final String HOST = "https://host"; - private static final String ACCOUNT = "account"; - private static final String ACCOUNT_ID = "account_id"; - private static final String DB_NAME = "dbName"; - private static final String ENGINE_NAME = "engineName"; - - @Spy - private final ObjectMapper objectMapper = new ObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - private final ObjectMapper mapper = new ObjectMapper(); - @Captor - private ArgumentCaptor<Request> requestArgumentCaptor; - @Mock - private OkHttpClient httpClient; - @Mock - private Call call; - private FireboltAccountClient fireboltAccountClient; - - @Mock - private FireboltConnection fireboltConnection; - - @BeforeEach - void setUp() { - fireboltAccountClient = new FireboltAccountClient(httpClient, objectMapper, fireboltConnection, "ConnA:1.0.9", - "ConnB:2.0.9"); - when(httpClient.newCall(any())).thenReturn(call); - } - - @Test - void shouldGetAccountId() throws Exception { - Response response = mock(Response.class); - when(response.code()).thenReturn(HTTP_OK); - ResponseBody body = mock(ResponseBody.class); - when(body.string()).thenReturn("{\"account_id\":\"12345\"}"); - when(response.body()).thenReturn(body); - when(call.execute()).thenReturn(response); - - FireboltAccountResponse account = fireboltAccountClient.getAccount(HOST, ACCOUNT, ACCESS_TOKEN); - - Map<String, String> expectedHeader = ImmutableMap.of("User-Agent", - userAgent("ConnB/2.0.9 JDBC/%s (Java %s; %s %s; ) ConnA/1.0.9"), "Authorization", - "Bearer " + ACCESS_TOKEN); - - verify(httpClient).newCall(requestArgumentCaptor.capture()); - verify(objectMapper).readValue("{\"account_id\":\"12345\"}", FireboltAccountResponse.class); - assertEquals("https://host/iam/v2/accounts:getIdByName?accountName=" + ACCOUNT, - requestArgumentCaptor.getValue().url().toString()); - assertEquals(expectedHeader, extractHeadersMap(requestArgumentCaptor.getValue())); - assertEquals("12345", account.getAccountId()); - } - - @Test - void shouldGetEngineEndpoint() throws Exception { - Response response = mock(Response.class); - when(response.code()).thenReturn(HTTP_OK); - ResponseBody body = mock(ResponseBody.class); - when(response.body()).thenReturn(body); - when(call.execute()).thenReturn(response); - when(response.body().string()).thenReturn(mapper.writeValueAsString(FireboltEngineResponse.builder() - .engine(FireboltEngineResponse.Engine.builder().endpoint("http://engineEndpoint").build()).build())); - when(httpClient.newCall(any())).thenReturn(call); - - FireboltEngineResponse engine = fireboltAccountClient.getEngine(HOST, ENGINE_NAME, DB_NAME, ACCOUNT_ID, - ACCESS_TOKEN); - Map<String, String> expectedHeader = ImmutableMap.of("User-Agent", - userAgent("ConnB/2.0.9 JDBC/%s (Java %s; %s %s; ) ConnA/1.0.9"), "Authorization", - "Bearer " + ACCESS_TOKEN); - - verify(httpClient).newCall(requestArgumentCaptor.capture()); - verify(objectMapper).readValue("{\"engine\":{\"endpoint\":\"http://engineEndpoint\",\"current_status\":null}}", - FireboltEngineResponse.class); - verify(httpClient).newCall(requestArgumentCaptor.capture()); - assertEquals("https://host/core/v1/accounts/engineName/engines/" + ACCOUNT_ID, - requestArgumentCaptor.getValue().url().toString()); - assertEquals(expectedHeader, extractHeadersMap(requestArgumentCaptor.getValue())); - assertEquals("http://engineEndpoint", engine.getEngine().getEndpoint()); - } - - @Test - void shouldGetDbAddress() throws Exception { - Response response = mock(Response.class); - when(response.code()).thenReturn(HTTP_OK); - ResponseBody body = mock(ResponseBody.class); - when(response.body()).thenReturn(body); - when(call.execute()).thenReturn(response); - when(response.body().string()).thenReturn(mapper - .writeValueAsString(FireboltDefaultDatabaseEngineResponse.builder().engineUrl("http://dbAddress").build())); - - FireboltDefaultDatabaseEngineResponse fireboltDefaultDatabaseEngineResponse = fireboltAccountClient - .getDefaultEngineByDatabaseName(HOST, ACCOUNT_ID, DB_NAME, ACCESS_TOKEN); - Map<String, String> expectedHeader = ImmutableMap.of("User-Agent", - userAgent("ConnB/2.0.9 JDBC/%s (Java %s; %s %s; ) ConnA/1.0.9"), "Authorization", - "Bearer " + ACCESS_TOKEN); - - verify(httpClient).newCall(requestArgumentCaptor.capture()); - verify(objectMapper).readValue("{\"engine_url\":\"http://dbAddress\"}", FireboltDefaultDatabaseEngineResponse.class); - assertEquals(format("https://host/core/v1/accounts/%s/engines:getURLByDatabaseName?databaseName=%s", - ACCOUNT_ID, DB_NAME), requestArgumentCaptor.getValue().url().toString()); - assertEquals(expectedHeader, extractHeadersMap(requestArgumentCaptor.getValue())); - assertEquals("http://dbAddress", fireboltDefaultDatabaseEngineResponse.getEngineUrl()); - } - - @Test - void shouldGetEngineId() throws Exception { - Response response = mock(Response.class); - when(response.code()).thenReturn(HTTP_OK); - ResponseBody body = mock(ResponseBody.class); - when(response.body()).thenReturn(body); - when(call.execute()).thenReturn(response); - when(response.body().string()).thenReturn(mapper.writeValueAsString(FireboltEngineIdResponse.builder() - .engine(FireboltEngineIdResponse.Engine.builder().engineId("13").build()).build())); - - FireboltEngineIdResponse fireboltEngineIdResponse = fireboltAccountClient.getEngineId(HOST, ACCOUNT_ID, - ENGINE_NAME, ACCESS_TOKEN); - Map<String, String> expectedHeader = ImmutableMap.of("User-Agent", - userAgent("ConnB/2.0.9 JDBC/%s (Java %s; %s %s; ) ConnA/1.0.9"), "Authorization", - "Bearer " + ACCESS_TOKEN); - - verify(httpClient).newCall(requestArgumentCaptor.capture()); - verify(objectMapper).readValue("{\"engine_id\":{\"engine_id\":\"13\"}}", FireboltEngineIdResponse.class); - assertEquals(format("https://host/core/v1/accounts/%s/engines:getIdByName?engine_name=%s", ACCOUNT_ID, - ENGINE_NAME), requestArgumentCaptor.getValue().url().toString()); - assertEquals(expectedHeader, extractHeadersMap(requestArgumentCaptor.getValue())); - assertEquals("13", fireboltEngineIdResponse.getEngine().getEngineId()); - } - - @Test - void shouldThrowExceptionWhenStatusCodeIsNotFound() throws Exception { - shouldThrowException(HTTP_NOT_FOUND, () -> fireboltAccountClient.getAccount(HOST, ACCOUNT, ACCESS_TOKEN), null); - } - - @Test - void shouldThrowExceptionWhenStatusCodeIsNotOk() throws Exception { - shouldThrowException(HTTP_BAD_GATEWAY, () -> fireboltAccountClient.getAccount(HOST, ACCOUNT, ACCESS_TOKEN), null); - } - - @Test - void shouldThrowExceptionWithDBNotFoundErrorMessageWhenDBIsNotFound() throws Exception { - shouldThrowException(HTTP_NOT_FOUND, () -> fireboltAccountClient.getDefaultEngineByDatabaseName(HOST, ACCOUNT, DB_NAME, ACCESS_TOKEN), "The database with the name dbName could not be found"); - } - - @Test - void shouldThrowExceptionWithEngineNotFoundErrorMessageWhenEngineAddressIsNotFound() throws Exception { - shouldThrowException(HTTP_NOT_FOUND, () -> fireboltAccountClient.getEngine(HOST, ACCOUNT, ENGINE_NAME, "123", ACCESS_TOKEN), "The address of the engine with name engineName and id 123 could not be found"); - } - - @Test - void shouldThrowExceptionWithEngineNotFoundErrorMessageWhenEngineIdIsNotFound() throws Exception { - shouldThrowException(HTTP_NOT_FOUND, () -> fireboltAccountClient.getEngineId(HOST, ACCOUNT, ENGINE_NAME, ACCESS_TOKEN), "The engine engineName could not be found"); - } - - private void shouldThrowException(int httpStatus, ThrowingRunnable runnable, String expectedMessage) throws Exception { - Response response = mock(Response.class); - when(response.code()).thenReturn(httpStatus); - ResponseBody body = mock(ResponseBody.class); - when(response.body()).thenReturn(body); - when(call.execute()).thenReturn(response); - FireboltException fireboltException = assertThrows(FireboltException.class, runnable); - if (expectedMessage != null) { - assertEquals(expectedMessage, fireboltException.getMessage()); - } - } - - private Map<String, String> extractHeadersMap(Request request) { - Map<String, String> headers = new HashMap<>(); - request.headers().forEach(header -> headers.put(header.getFirst(), header.getSecond())); - return headers; - } -} -======= ->>>>>>> 5242315 (Start adding support for new identity) diff --git a/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java b/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java index e2c887f5d..5d705858e 100644 --- a/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java +++ b/src/test/java/com/firebolt/jdbc/client/authentication/ServiceAccountAuthenticationRequestTest.java @@ -1,8 +1,11 @@ package com.firebolt.jdbc.client.authentication; +import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; @@ -19,7 +22,7 @@ void shouldCreateHttpEntityWithTheProvidedCredentials() throws IOException { Buffer buffer = new Buffer(); requestBody.writeTo(buffer); - assertEquals("audience=https%3A%2F%2Fdev-firebolt-v2.us.auth0.com%2Fapi%2Fv2%2F&grant_type=client_credentials&client_id=he-ll-o&client_secret=secret", buffer.readUtf8()); + assertEquals(format("audience=%s&grant_type=client_credentials&client_id=he-ll-o&client_secret=secret", URLEncoder.encode("https://api.firebolt.io", StandardCharsets.UTF_8)), buffer.readUtf8()); } } \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java index 27b666790..4fcb1639c 100644 --- a/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java +++ b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java @@ -1,35 +1,32 @@ package com.firebolt.jdbc.client.gateway; -import static java.net.HttpURLConnection.HTTP_NOT_FOUND; -import static java.net.HttpURLConnection.HTTP_OK; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; - -import com.firebolt.jdbc.exception.ExceptionType; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.firebolt.jdbc.client.account.FireboltAccount; +import com.firebolt.jdbc.client.account.FireboltAccountRetriever; import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; - import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Response; import okhttp3.ResponseBody; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static java.net.HttpURLConnection.HTTP_OK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -class FireboltGatewayUrlClientTest { +class FireboltAccountRetrieverTest { @Spy private final ObjectMapper objectMapper = new ObjectMapper(); @@ -40,24 +37,45 @@ class FireboltGatewayUrlClientTest { @Mock private FireboltConnection fireboltConnection; - @InjectMocks - private FireboltGatewayUrlClient fireboltGatewayUrlClient; + private FireboltAccountRetriever<GatewayUrlResponse> fireboltGatewayUrlClient; + private FireboltAccountRetriever<FireboltAccount> fireboltAccountIdResolver; + + + @BeforeEach + void setUp() { + fireboltGatewayUrlClient = new FireboltAccountRetriever<>(httpClient, fireboltConnection, null, null, objectMapper, "engineUrl", GatewayUrlResponse.class); + fireboltAccountIdResolver = new FireboltAccountRetriever<>(httpClient, fireboltConnection, null, null, objectMapper, "resolve", FireboltAccount.class); + } @Test void shouldGetGatewayUrlWhenResponseIsOk() throws IOException, FireboltException { GatewayUrlResponse response = GatewayUrlResponse.builder().engineUrl("http://engine").build(); injectMockedResponse(httpClient, HTTP_OK, response); - assertEquals("http://engine", fireboltGatewayUrlClient.getGatewayUrl("access_token", "account").getEngineUrl()); + assertEquals("http://engine", fireboltGatewayUrlClient.retrieve("access_token", "account").getEngineUrl()); } @Test - void shouldThrowFireboltExceptionUponException() { + void shouldGetAccountId() throws IOException, FireboltException { + FireboltAccount account = new FireboltAccount("12345", "central"); + injectMockedResponse(httpClient, HTTP_OK, account); + assertEquals(account, fireboltAccountIdResolver.retrieve("access_token", "account")); + } + + @Test + void shouldRuntimeExceptionUponRuntimeException() throws FireboltException { when(httpClient.newCall(any())).thenThrow(new IllegalArgumentException("ex")); - Exception ex = assertThrows(FireboltException.class, () ->fireboltGatewayUrlClient.getGatewayUrl("token", "acc")); - assertEquals("Failed to get gateway url for account acc", ex.getMessage()); + assertEquals("ex", assertThrows(IllegalArgumentException.class, () -> fireboltGatewayUrlClient.retrieve("token", "acc")).getMessage()); + } + + @Test + void shouldThrowFireboltExceptionUponIOException() throws IOException { + Call call = mock(Call.class); + when(httpClient.newCall(any())).thenReturn(call); + when(call.execute()).thenThrow(new IOException("ex")); + assertEquals("Failed to get engineUrl url for account acc", assertThrows(FireboltException.class, () -> fireboltGatewayUrlClient.retrieve("token", "acc")).getMessage()); } - private void injectMockedResponse(OkHttpClient httpClient, int code, GatewayUrlResponse gatewayUrlResponse) throws IOException { + private void injectMockedResponse(OkHttpClient httpClient, int code, Object payload) throws IOException { Response response = mock(Response.class); Call call = mock(Call.class); when(httpClient.newCall(any())).thenReturn(call); @@ -65,8 +83,7 @@ private void injectMockedResponse(OkHttpClient httpClient, int code, GatewayUrlR ResponseBody body = mock(ResponseBody.class); when(response.body()).thenReturn(body); when(response.code()).thenReturn(code); - String gatewayResponse = new ObjectMapper() - .writeValueAsString(gatewayUrlResponse); + String gatewayResponse = new ObjectMapper().writeValueAsString(payload); when(body.string()).thenReturn(gatewayResponse); } } \ No newline at end of file diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index bf4a7c0ad..425080211 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -74,7 +74,7 @@ void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { @Test void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, URISyntaxException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db1").compress(true) - .host("firebolt1").port(555).build(); + .host("firebolt1").port(555).accountId("12345").build(); when(connection.getAccessToken()) .thenReturn(Optional.of("token")); StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), @@ -90,7 +90,7 @@ void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, String actualQuery = getActualRequestString(actualRequest); assertEquals("show databases;", actualQuery); - assertEquals("http://firebolt1:555/dynamic/query?output_format=TabSeparatedWithNamesAndTypes", + assertEquals("http://firebolt1:555/?account_id=12345&output_format=TabSeparatedWithNamesAndTypes", actualRequest.url().toString()); } diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index aa65e43a5..509ef5f26 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -1,35 +1,30 @@ package com.firebolt.jdbc.connection; -import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -import java.sql.*; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import com.firebolt.jdbc.service.FireboltEngineService; -import com.firebolt.jdbc.service.FireboltGatewayUrlService; +import com.firebolt.jdbc.connection.settings.FireboltProperties; +import com.firebolt.jdbc.exception.ExceptionType; +import com.firebolt.jdbc.exception.FireboltException; +import com.firebolt.jdbc.service.*; +import com.firebolt.jdbc.statement.StatementInfoWrapper; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension; -import com.firebolt.jdbc.connection.settings.FireboltProperties; -import com.firebolt.jdbc.exception.ExceptionType; -import com.firebolt.jdbc.exception.FireboltException; -import com.firebolt.jdbc.service.FireboltAuthenticationService; -import com.firebolt.jdbc.service.FireboltStatementService; -import com.firebolt.jdbc.statement.StatementInfoWrapper; +import java.sql.*; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class FireboltConnectionTest { @@ -53,154 +48,157 @@ class FireboltConnectionTest { private FireboltEngineService fireboltEngineService; @Mock private FireboltStatementService fireboltStatementService; + @Mock + private FireboltAccountIdService fireboltAccountIdService; private Properties connectionProperties = new Properties(); + private Engine engine; + @BeforeEach void init() throws FireboltException { connectionProperties = new Properties(); - connectionProperties.put("user", "user"); - connectionProperties.put("password", "pa$$word"); + connectionProperties.put("client_id", "somebody"); + connectionProperties.put("client_secret", "pa$$word"); connectionProperties.put("compress", "1"); lenient().when(fireboltAuthenticationService.getConnectionTokens(eq("https://api.dev.firebolt.io:443"), any())) .thenReturn(fireboltConnectionTokens); - lenient().when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("url"); + lenient().when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://foo:8080/bar"); + engine = new Engine("endpoint", "id123", "OK", "noname"); + lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(engine); } @Test void shouldInitConnection() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertFalse(fireboltConnection.isClosed()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertFalse(fireboltConnection.isClosed()); + } } @Test void shouldNotFetchTokenNorEngineHostForLocalFirebolt() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(LOCAL_URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - verifyNoInteractions(fireboltAuthenticationService); - verifyNoInteractions(fireboltGatewayUrlService); - assertFalse(fireboltConnection.isClosed()); + try (FireboltConnection fireboltConnection = createConnection(LOCAL_URL, connectionProperties)) { + verifyNoInteractions(fireboltAuthenticationService); + verifyNoInteractions(fireboltGatewayUrlService); + assertFalse(fireboltConnection.isClosed()); + } } @Test void shouldPrepareStatement() throws SQLException { when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) .thenReturn(Optional.empty()); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - PreparedStatement statement = fireboltConnection - .prepareStatement("INSERT INTO cars(sales, name) VALUES (?, ?)"); - statement.setObject(1, 500); - statement.setObject(2, "Ford"); - statement.execute(); - assertNotNull(fireboltConnection); - assertNotNull(statement); - verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any()); - assertEquals("INSERT INTO cars(sales, name) VALUES (500, 'Ford')", - queryInfoWrapperArgumentCaptor.getValue().getSql()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + PreparedStatement statement = fireboltConnection + .prepareStatement("INSERT INTO cars(sales, name) VALUES (?, ?)"); + statement.setObject(1, 500); + statement.setObject(2, "Ford"); + statement.execute(); + assertNotNull(fireboltConnection); + assertNotNull(statement); + verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); + assertEquals("INSERT INTO cars(sales, name) VALUES (500, 'Ford')", + queryInfoWrapperArgumentCaptor.getValue().getSql()); + } } @Test void shouldCloseAllStatementsOnClose() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - Statement statement = fireboltConnection.createStatement(); - Statement preparedStatement = fireboltConnection.prepareStatement("test"); - fireboltConnection.close(); - assertTrue(statement.isClosed()); - assertTrue(preparedStatement.isClosed()); - assertTrue(fireboltConnection.isClosed()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + Statement statement = fireboltConnection.createStatement(); + Statement preparedStatement = fireboltConnection.prepareStatement("test"); + fireboltConnection.close(); + assertTrue(statement.isClosed()); + assertTrue(preparedStatement.isClosed()); + assertTrue(fireboltConnection.isClosed()); + } } @Test void shouldNotSetNewPropertyWhenConnectionIsNotValidWithTheNewProperty() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - - when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) - .thenThrow(new FireboltException(ExceptionType.TOO_MANY_REQUESTS)); - assertThrows(FireboltException.class, - () -> fireboltConnection.addProperty(new ImmutablePair<>("custom_1", "1"))); - - verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); - assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); - assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); - assertNull(fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) + .thenThrow(new FireboltException(ExceptionType.TOO_MANY_REQUESTS)); + assertThrows(FireboltException.class, + () -> fireboltConnection.addProperty(new ImmutablePair<>("custom_1", "1"))); + + verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); + assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); + assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); + assertNull(fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); + } } @Test void shouldSetNewPropertyWhenConnectionIsValidWithTheNewProperty() throws SQLException { when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(),anyBoolean(), any())) .thenReturn(Optional.empty()); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - Pair<String, String> newProperties = new ImmutablePair<>("custom_1", "1"); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + Pair<String, String> newProperties = new ImmutablePair<>("custom_1", "1"); - fireboltConnection.addProperty(newProperties); + fireboltConnection.addProperty(newProperties); - verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); - assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); - assertEquals("1", fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); - assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); + verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); + assertEquals("1", propertiesArgumentCaptor.getValue().getAdditionalProperties().get("custom_1")); + assertEquals("1", fireboltConnection.getSessionProperties().getAdditionalProperties().get("custom_1")); + assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); + } } @Test void shouldValidateConnectionWhenCallingIsValid() throws SQLException { when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenReturn(Optional.empty()); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - fireboltConnection.isValid(500); - - verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), - propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); - assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + fireboltConnection.isValid(500); + verify(fireboltStatementService).execute(queryInfoWrapperArgumentCaptor.capture(), + propertiesArgumentCaptor.capture(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any()); + assertEquals("SELECT 1", queryInfoWrapperArgumentCaptor.getValue().getSql()); + } } @Test void shouldNotValidateConnectionWhenCallingIsValidWhenUsingSystemEngine() throws SQLException { Properties propertiesWithSystemEngine = new Properties(connectionProperties); - FireboltConnection fireboltConnection = new FireboltConnection(SYSTEM_ENGINE_URL, propertiesWithSystemEngine, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - fireboltConnection.isValid(500); - - verifyNoInteractions(fireboltStatementService); + try (FireboltConnection fireboltConnection = createConnection(SYSTEM_ENGINE_URL, propertiesWithSystemEngine)) { + fireboltConnection.isValid(500); + verifyNoInteractions(fireboltStatementService); + } } @Test void shouldIgnore429WhenValidatingConnection() throws SQLException { when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenThrow(new FireboltException(ExceptionType.TOO_MANY_REQUESTS)); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertTrue(fireboltConnection.isValid(500)); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertTrue(fireboltConnection.isValid(500)); + } } @Test void shouldReturnFalseWhenValidatingConnectionThrowsAnException() throws SQLException { when(fireboltStatementService.execute(any(), any(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any())) .thenThrow(new FireboltException(ExceptionType.ERROR)); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertFalse(fireboltConnection.isValid(500)); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertFalse(fireboltConnection.isValid(500)); + } } @Test void shouldThrowExceptionWhenValidatingConnectionWithNegativeTimeout() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertThrows(FireboltException.class, () -> fireboltConnection.isValid(-1)); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertThrows(FireboltException.class, () -> fireboltConnection.isValid(-1)); + } } @Test void shouldReturnFalseWhenValidatingClosedConnection() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - fireboltConnection.close(); - assertFalse(fireboltConnection.isValid(50)); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + fireboltConnection.close(); + assertFalse(fireboltConnection.isValid(50)); + } } @Test @@ -210,69 +208,66 @@ void shouldExtractConnectorOverrides() throws SQLException { connectionProperties.put("user_clients", "ConnA:1.0.9,ConnB:2.8.0"); connectionProperties.put("user_drivers", "DriverA:2.0.9,DriverB:3.8.0"); - FireboltConnection fireboltConnectionImpl = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - - PreparedStatement statement = fireboltConnectionImpl.prepareStatement("SELECT 1"); - statement.execute(); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + PreparedStatement statement = fireboltConnection.prepareStatement("SELECT 1"); + statement.execute(); - verify(fireboltStatementService).execute(any(), propertiesArgumentCaptor.capture(), anyInt(), anyInt(), - anyBoolean(), anyBoolean(), any()); - assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_clients")); - assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_drivers")); - assertNull(fireboltConnectionImpl.getSessionProperties().getAdditionalProperties().get("user_clients")); - assertNull(fireboltConnectionImpl.getSessionProperties().getAdditionalProperties().get("user_drivers")); + verify(fireboltStatementService).execute(any(), propertiesArgumentCaptor.capture(), anyInt(), anyInt(), + anyBoolean(), anyBoolean(), any()); + assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_clients")); + assertNull(propertiesArgumentCaptor.getValue().getAdditionalProperties().get("user_drivers")); + assertNull(fireboltConnection.getSessionProperties().getAdditionalProperties().get("user_clients")); + assertNull(fireboltConnection.getSessionProperties().getAdditionalProperties().get("user_drivers")); + } } @Test - @Disabled("System engine is used until engine_url is available") void shouldGetEngineNameFromHost() throws SQLException { connectionProperties.put("engine", "hello"); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals("hello", fireboltConnection.getEngine()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(engine.getName(), fireboltConnection.getEngine()); + } } @Test void shouldInitNetworkTimeoutWithPropertyByDefault() throws SQLException { connectionProperties.put("socket_timeout_millis", "60"); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals(60, fireboltConnection.getNetworkTimeout()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(60, fireboltConnection.getNetworkTimeout()); + } } @Test void shouldInitConnectionTimeoutWithPropertyByDefault() throws SQLException { connectionProperties.put("connection_timeout_millis", "50"); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals(50, fireboltConnection.getConnectionTimeout()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(50, fireboltConnection.getConnectionTimeout()); + } } @Test void shouldCloseConnectionWhenAbortingConnection() throws SQLException, InterruptedException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - ExecutorService executorService = Executors.newFixedThreadPool(10); - fireboltConnection.abort(executorService); - executorService.awaitTermination(1, TimeUnit.SECONDS); - assertTrue(fireboltConnection.isClosed()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + ExecutorService executorService = Executors.newFixedThreadPool(10); + fireboltConnection.abort(executorService); + executorService.awaitTermination(1, TimeUnit.SECONDS); + assertTrue(fireboltConnection.isClosed()); + } } @Test void shouldRemoveExpiredToken() throws SQLException { - FireboltProperties fireboltProperties = FireboltProperties.builder().host("host").path("/db").port(8080) - .build(); + FireboltProperties fireboltProperties = FireboltProperties.builder().host("host").path("/db").port(8080).build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); when(fireboltAuthenticationService.getConnectionTokens("http://host:8080", fireboltProperties)) .thenReturn(FireboltConnectionTokens.builder().build()); lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://hello").build()); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - fireboltConnection.removeExpiredTokens(); - verify(fireboltAuthenticationService).removeConnectionTokens("http://host:8080", fireboltProperties); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + fireboltConnection.removeExpiredTokens(); + verify(fireboltAuthenticationService).removeConnectionTokens("http://host:8080", fireboltProperties); + } } } @@ -287,10 +282,10 @@ void shouldReturnConnectionTokenWhenAvailable() throws SQLException { when(fireboltAuthenticationService.getConnectionTokens(eq("http://host:8080"), any())) .thenReturn(connectionTokens); lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://engineHost").build()); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - verify(fireboltAuthenticationService).getConnectionTokens("http://host:8080", fireboltProperties); - assertEquals(accessToken, fireboltConnection.getAccessToken().get()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + verify(fireboltAuthenticationService).getConnectionTokens("http://host:8080", fireboltProperties); + assertEquals(accessToken, fireboltConnection.getAccessToken().get()); + } } } @@ -300,10 +295,10 @@ void shouldNotReturnConnectionTokenWithLocalDb() throws SQLException { .build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals(Optional.empty(), fireboltConnection.getAccessToken()); - verifyNoInteractions(fireboltAuthenticationService); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(Optional.empty(), fireboltConnection.getAccessToken()); + verifyNoInteractions(fireboltAuthenticationService); + } } } @@ -313,57 +308,53 @@ void shouldSetNetworkTimeout() throws SQLException { .socketTimeoutMillis(5).port(8080).build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals(5, fireboltConnection.getNetworkTimeout()); - fireboltConnection.setNetworkTimeout(null, 1); - assertEquals(1, fireboltConnection.getNetworkTimeout()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(5, fireboltConnection.getNetworkTimeout()); + fireboltConnection.setNetworkTimeout(null, 1); + assertEquals(1, fireboltConnection.getNetworkTimeout()); + } } } @Test void shouldUseConnectionTimeoutFromProperties() throws SQLException { - FireboltProperties fireboltProperties = FireboltProperties.builder().host("localhost").path("/db") - .connectionTimeoutMillis(20).port(8080).build(); + FireboltProperties fireboltProperties = FireboltProperties.builder().host("localhost").path("/db").connectionTimeoutMillis(20).port(8080).build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertEquals(20, fireboltConnection.getConnectionTimeout()); + try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { + assertEquals(20, fireboltConnection.getConnectionTimeout()); + } } } @Test void shouldThrowExceptionWhenTryingToUseClosedConnection() throws SQLException { - FireboltConnection fireboltConnection = new FireboltConnection(URL, connectionProperties, - fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - fireboltConnection.close(); - assertThrows(FireboltException.class, fireboltConnection::getCatalog); + try (Connection connection = createConnection(URL, connectionProperties)) { + connection.close(); + assertThrows(FireboltException.class, connection::getCatalog); + } } @Test void shouldUnwrapFireboltConnection() throws SQLException { - Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService); - assertTrue(connection.isWrapperFor(FireboltConnection.class)); - assertEquals(connection, connection.unwrap(FireboltConnection.class)); + try (Connection connection = createConnection(URL, connectionProperties)) { + assertTrue(connection.isWrapperFor(FireboltConnection.class)); + assertEquals(connection, connection.unwrap(FireboltConnection.class)); + } } @Test void shouldThrowExceptionWhenCannotUnwrap() throws SQLException { - try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + try (Connection connection = createConnection(URL, connectionProperties)) { assertFalse(connection.isWrapperFor(String.class)); assertThrows(SQLException.class, () -> connection.unwrap(String.class)); } } @Test - @Disabled("Db is currently not supported") void shouldGetDatabaseWhenGettingCatalog() throws SQLException { connectionProperties.put("database", "db"); - try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + try (Connection connection = createConnection(URL, connectionProperties)) { assertEquals("db", connection.getCatalog()); } } @@ -371,8 +362,7 @@ void shouldGetDatabaseWhenGettingCatalog() throws SQLException { @Test void shouldGetNoneTransactionIsolation() throws SQLException { connectionProperties.put("database", "db"); - try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + try (Connection connection = createConnection(URL, connectionProperties)) { assertEquals(Connection.TRANSACTION_NONE, connection.getTransactionIsolation()); } } @@ -380,48 +370,46 @@ void shouldGetNoneTransactionIsolation() throws SQLException { @Test void shouldThrowExceptionWhenPreparingStatementWIthInvalidResultSetType() throws SQLException { connectionProperties.put("database", "db"); - try (Connection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { + try (Connection connection = createConnection(URL, connectionProperties)) { assertThrows(SQLFeatureNotSupportedException.class, () -> connection.prepareStatement("any", TYPE_SCROLL_INSENSITIVE, 0)); } } @Test - @Disabled("Disabled until engine_url is available") void shouldGetDefaultEngineWhenEngineIsNotProvided() throws SQLException { connectionProperties.put("engine", ""); when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().name("default_engine").endpoint("http://my-endpoint").build()); - try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { - verify(fireboltEngineService).getEngine(null, "db"); + try (FireboltConnection connection = createConnection(URL, connectionProperties)) { + verify(fireboltEngineService).getEngine("", "db"); assertEquals("default_engine", connection.getSessionProperties().getEngine()); - assertEquals("my-endpoint", connection.getSessionProperties().getHost()); + assertEquals("http://my-endpoint", connection.getSessionProperties().getHost()); } } @Test - @Disabled("Disabled until engine_url is available") void shouldGetEngineUrlWhenEngineIsProvided() throws SQLException { connectionProperties.put("engine", "engine"); when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("http://my_endpoint").build()); - try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { - verify(fireboltEngineService).getEngine("engine", null); + try (FireboltConnection connection = createConnection(URL, connectionProperties)) { + verify(fireboltEngineService).getEngine("engine", "db"); assertEquals("http://my_endpoint", connection.getSessionProperties().getHost()); } } @Test - @Disabled("Db is currently not supported") void shouldNotGetEngineUrlOrDefaultEngineUrlWhenUsingSystemEngine() throws SQLException { connectionProperties.put("engine", "system"); + connectionProperties.put("database", "my_db"); when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://my_endpoint"); - try (FireboltConnection connection = new FireboltConnection(URL, connectionProperties, fireboltAuthenticationService, - fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService)) { - verify(fireboltEngineService, times(0)).getEngine(any(), any()); - assertEquals("http://my_endpoint", connection.getSessionProperties().getHost()); + try (FireboltConnection connection = createConnection(URL, connectionProperties)) { + verify(fireboltEngineService, times(0)).getEngine(eq("system"), eq("my_db")); + assertEquals("my_endpoint", connection.getSessionProperties().getHost()); } } + + private FireboltConnection createConnection(String url, Properties props) throws FireboltException { + return new FireboltConnection(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); + } } diff --git a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java index a583c8939..7b2875c6a 100644 --- a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java @@ -15,7 +15,7 @@ void shouldHaveDefaultPropertiesWhenOnlyTheRequiredFieldsAreSpecified() { FireboltProperties expectedDefaultProperties = FireboltProperties.builder().database("db").bufferSize(65536) .sslCertificatePath("").sslMode("strict").path("").port(443) // 443 by default as SSL is enabled by .systemEngine(false).compress(true) // default - .user(null).password(null).host("host").ssl(true).additionalProperties(new HashMap<>()) + .principal(null).secret(null).host("host").ssl(true).additionalProperties(new HashMap<>()) .account(null).keepAliveTimeoutMillis(300000).maxConnectionsTotal(300).maxRetries(3) .socketTimeoutMillis(0).connectionTimeoutMillis(60000).tcpKeepInterval(30).environment("app").tcpKeepIdle(60) .tcpKeepCount(10).account("firebolt").build(); @@ -45,7 +45,7 @@ void shouldHaveAllTheSpecifiedCustomProperties() { FireboltProperties expectedDefaultProperties = FireboltProperties.builder().bufferSize(51) .sslCertificatePath("root_cert").sslMode("none").path("example").database("myDb").compress(true) - .port(443).user(null).password(null).host("myDummyHost").ssl(true).systemEngine(false) + .port(443).principal(null).secret(null).host("myDummyHost").ssl(true).systemEngine(false) .additionalProperties(customProperties).account(null).keepAliveTimeoutMillis(300000) .maxConnectionsTotal(300).maxRetries(3).socketTimeoutMillis(20).connectionTimeoutMillis(60000) .tcpKeepInterval(30).tcpKeepIdle(60).tcpKeepCount(10).environment("app").account("firebolt").build(); diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java index 2a8700549..8c794388a 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltAuthenticationServiceTest.java @@ -28,8 +28,7 @@ class FireboltAuthenticationServiceTest { private static final String ENV = "ENV"; - private static final FireboltProperties PROPERTIES = FireboltProperties.builder().user(USER).password(PASSWORD).environment(ENV) - .compress(true).build(); + private static final FireboltProperties PROPERTIES = FireboltProperties.builder().principal(USER).secret(PASSWORD).environment(ENV).compress(true).build(); @Mock private FireboltAuthenticationClient fireboltAuthenticationClient; diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java index 37dd345ba..412ee2daa 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java @@ -1,24 +1,23 @@ package com.firebolt.jdbc.service; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - +import com.firebolt.jdbc.connection.Engine; +import com.firebolt.jdbc.connection.FireboltConnection; +import com.firebolt.jdbc.exception.FireboltException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import com.firebolt.jdbc.connection.Engine; -import com.firebolt.jdbc.connection.FireboltConnection; -import com.firebolt.jdbc.exception.FireboltException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class FireboltEngineServiceTest { @@ -46,50 +45,50 @@ void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromNullHost() { @Test void shouldGetDefaultEngineWhenEngineNameIsNotProvided() throws SQLException { - Statement statement = mock(Statement.class); + PreparedStatement statement = mock(PreparedStatement.class); ResultSet resultSet = mock(ResultSet.class); when(resultSet.next()).thenReturn(true); when(resultSet.getString("status")).thenReturn("running"); - when(resultSet.getString("engine_url")).thenReturn("https://url"); + when(resultSet.getString("url")).thenReturn("https://url"); when(resultSet.getString("engine_name")).thenReturn("hello-engine"); - when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); - when(statement.executeQuery(any())).thenReturn(resultSet); + when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); assertEquals(Engine.builder().endpoint("https://url").name("hello-engine").build(), fireboltEngineService.getEngine(null, "db")); } @Test void shouldThrowExceptionWhenDefaultEngineNotRunning() throws SQLException { - Statement statement = mock(Statement.class); + PreparedStatement statement = mock(PreparedStatement.class); ResultSet resultSet = mock(ResultSet.class); when(resultSet.next()).thenReturn(true); when(resultSet.getString("status")).thenReturn("down"); - when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); - when(statement.executeQuery(any())).thenReturn(resultSet); + when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(null, "db")); } @Test void shouldGetEngineWhenEngineNameIsProvided() throws SQLException { - Statement statement = mock(Statement.class); + PreparedStatement statement = mock(PreparedStatement.class); ResultSet resultSet = mock(ResultSet.class); when(resultSet.next()).thenReturn(true); when(resultSet.getString("status")).thenReturn("running"); - when(resultSet.getString("engine_url")).thenReturn("https://url"); - when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); - when(statement.executeQuery(any())).thenReturn(resultSet); + when(resultSet.getString("url")).thenReturn("https://url"); + when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); assertEquals(Engine.builder().endpoint("https://url").name("some-engine").build(), fireboltEngineService.getEngine("some-engine", "db")); } @Test void shouldThrowExceptionWhenEngineNotRunning() throws SQLException { - Statement statement = mock(Statement.class); + PreparedStatement statement = mock(PreparedStatement.class); ResultSet resultSet = mock(ResultSet.class); when(resultSet.next()).thenReturn(true); when(resultSet.getString("status")).thenReturn("down"); - when(fireboltConnection.createSystemEngineStatementStatement()).thenReturn(statement); - when(statement.executeQuery(any())).thenReturn(resultSet); + when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine("some-engine", "db")); } From 5ecf60f2bd663be6ae3a5ab66bcb2fcd6c257f25 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Sun, 9 Jul 2023 15:35:15 +0300 Subject: [PATCH 03/15] applied pr comments --- .github/workflows/integration-test.yml | 6 ++++- .../integration/tests/NumericTypesTest.java | 24 +++++++++++++++++++ .../integration/tests/SystemEngineTest.java | 2 +- .../java/com/firebolt/FireboltDriver.java | 3 +-- .../firebolt/jdbc/client/FireboltClient.java | 7 +++--- .../account/FireboltAccountRetriever.java | 10 ++++---- .../FireboltAuthenticationClient.java | 2 +- .../client/query/StatementClientImpl.java | 6 ++--- .../jdbc/connection/FireboltConnection.java | 12 ++++++---- .../settings/FireboltProperties.java | 22 +++++++++++++++-- .../gateway/FireboltGatewayUrlClientTest.java | 4 ++-- .../client/query/StatementClientImplTest.java | 12 +++++----- .../settings/FireboltPropertiesTest.java | 24 +++++++++++++++++++ 13 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 src/integrationTest/java/integration/tests/NumericTypesTest.java diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 21cd1a41a..5f4d34bed 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,9 +7,13 @@ on: description: 'Database - a new one will be created if not provided' required: false default: '' + engine: + description: 'Engine' + required: true account: description: 'Account' required: true + default: 'integration' environment: description: 'Environment to run the tests against' type: choice @@ -69,7 +73,7 @@ jobs: fi - name: Run integration tests - run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dpassword="${{ env.SERVICE_ACCOUNT_SECRET }}" -Duser="${{ env.SERVICE_ACCOUNT_ID }} -Daccount=${{ github.event.inputs.account }}" + run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }} -Daccount=${{ github.event.inputs.account }}" -Dengine=${{ github.event.inputs.engine }}" - name: "Foresight: Analyze Test Results" uses: runforesight/foresight-test-kit-action@v1 diff --git a/src/integrationTest/java/integration/tests/NumericTypesTest.java b/src/integrationTest/java/integration/tests/NumericTypesTest.java new file mode 100644 index 000000000..c0c2dda5b --- /dev/null +++ b/src/integrationTest/java/integration/tests/NumericTypesTest.java @@ -0,0 +1,24 @@ +package integration.tests; + +import integration.IntegrationTest; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class NumericTypesTest extends IntegrationTest { + @Test + void shouldHaveCorrectInfo() throws SQLException { + try (Connection connection = this.createConnection("system"); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("SELECT 3::decimal")) { + resultSet.next(); + assertEquals(9, resultSet.getMetaData().getScale(1)); + assertEquals(38, resultSet.getMetaData().getPrecision(1)); + } + } +} diff --git a/src/integrationTest/java/integration/tests/SystemEngineTest.java b/src/integrationTest/java/integration/tests/SystemEngineTest.java index 80870eade..cd596d09d 100644 --- a/src/integrationTest/java/integration/tests/SystemEngineTest.java +++ b/src/integrationTest/java/integration/tests/SystemEngineTest.java @@ -41,7 +41,7 @@ void afterAll() { @Test void shouldExecuteQueriesUsingSystemEngine() throws SQLException { try (Connection connection = this.createConnection(SYSTEM_ENGINE_NAME)) { - List<String> queries = Arrays.asList(String.format("CREATE DATABASE %s", DATABASE_NAME), + List<String> queries = Arrays.asList(String.format("CREATE DATABASE IF NOT EXISTS %s", DATABASE_NAME), String.format("CREATE ENGINE %s", ENGINE_NAME), String.format("ATTACH ENGINE %s TO %s;", ENGINE_NAME, DATABASE_NAME), String.format("ALTER DATABASE %s WITH DESCRIPTION = 'JDBC Integration test'", DATABASE_NAME), diff --git a/src/main/java/com/firebolt/FireboltDriver.java b/src/main/java/com/firebolt/FireboltDriver.java index 15e248c1b..1794d615b 100644 --- a/src/main/java/com/firebolt/FireboltDriver.java +++ b/src/main/java/com/firebolt/FireboltDriver.java @@ -17,7 +17,6 @@ public class FireboltDriver implements Driver { public static final String JDBC_FIREBOLT = "jdbc:firebolt:"; - private static final String JDBC_FIREBOLT_PREFIX = JDBC_FIREBOLT; static { try { @@ -35,7 +34,7 @@ public Connection connect(String url, Properties connectionSettings) throws SQLE @Override public boolean acceptsURL(String url) { - return StringUtils.isNotEmpty(url) && url.startsWith(JDBC_FIREBOLT_PREFIX); + return StringUtils.isNotEmpty(url) && url.startsWith(JDBC_FIREBOLT); } @Override diff --git a/src/main/java/com/firebolt/jdbc/client/FireboltClient.java b/src/main/java/com/firebolt/jdbc/client/FireboltClient.java index a520f12f5..aab78cc55 100644 --- a/src/main/java/com/firebolt/jdbc/client/FireboltClient.java +++ b/src/main/java/com/firebolt/jdbc/client/FireboltClient.java @@ -32,16 +32,15 @@ public abstract class FireboltClient { public static final String HEADER_AUTHORIZATION = "Authorization"; public static final String HEADER_AUTHORIZATION_BEARER_PREFIX_VALUE = "Bearer "; public static final String HEADER_USER_AGENT = "User-Agent"; + private final OkHttpClient httpClient; protected final ObjectMapper objectMapper; private final String headerUserAgentValue; - private final OkHttpClient httpClient; private final FireboltConnection connection; - protected FireboltClient(OkHttpClient httpClient, FireboltConnection connection, String customDrivers, - String customClients, ObjectMapper objectMapper) { + protected FireboltClient(OkHttpClient httpClient, ObjectMapper objectMapper, FireboltConnection connection, String customDrivers, String customClients) { this.httpClient = httpClient; - this.connection = connection; this.objectMapper = objectMapper; + this.connection = connection; this.headerUserAgentValue = UsageTrackerUtil.getUserAgentString(customDrivers != null ? customDrivers : "", customClients != null ? customClients : ""); } diff --git a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java index cc7936877..c99efc6f7 100644 --- a/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java +++ b/src/main/java/com/firebolt/jdbc/client/account/FireboltAccountRetriever.java @@ -11,19 +11,21 @@ import static java.lang.String.format; public class FireboltAccountRetriever<T> extends FireboltClient { - private static final String URL = "https://api.dev.firebolt.io/web/v3/account/%s/%s"; + private static final String URL = "https://%s/web/v3/account/%s/%s"; + private final String host; private final String path; private final Class<T> type; - public FireboltAccountRetriever(OkHttpClient httpClient, FireboltConnection connection, String customDrivers, String customClients, ObjectMapper objectMapper, String path, Class<T> type) { - super(httpClient, connection, customDrivers, customClients, objectMapper); + public FireboltAccountRetriever(OkHttpClient httpClient, ObjectMapper objectMapper, FireboltConnection connection, String customDrivers, String customClients, String host, String path, Class<T> type) { + super(httpClient, objectMapper, connection, customDrivers, customClients); + this.host = host; this.path = path; this.type = type; } public T retrieve(String accessToken, String accountName) throws FireboltException { try { - return getResource(format(URL, accountName, path), accessToken, type); + return getResource(format(URL, host, accountName, path), accessToken, type); } catch (IOException e) { throw new FireboltException(String.format("Failed to get %s url for account %s", path, accountName), e); } diff --git a/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java b/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java index a63d00747..d9a9cb30b 100644 --- a/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java +++ b/src/main/java/com/firebolt/jdbc/client/authentication/FireboltAuthenticationClient.java @@ -21,7 +21,7 @@ public class FireboltAuthenticationClient extends FireboltClient { public FireboltAuthenticationClient(OkHttpClient httpClient, ObjectMapper objectMapper, FireboltConnection connection, String customDrivers, String customClients) { - super(httpClient, connection, customDrivers, customClients, objectMapper); + super(httpClient, objectMapper, connection, customDrivers, customClients); } /** diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index b23464ca9..83a8ac740 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -42,9 +42,8 @@ public class StatementClientImpl extends FireboltClient implements StatementClie private final BiPredicate<Call, String> isCallWithId = (call, id) -> call.request().tag() instanceof String && StringUtils.equals((String) call.request().tag(), id); - public StatementClientImpl(OkHttpClient httpClient, FireboltConnection connection, ObjectMapper objectMapper, - String customDrivers, String customClients) { - super(httpClient, connection, customDrivers, customClients, objectMapper); + public StatementClientImpl(OkHttpClient httpClient, ObjectMapper objectMapper, FireboltConnection connection, String customDrivers, String customClients) { + super(httpClient, objectMapper, connection, customDrivers, customClients); } /** @@ -208,7 +207,6 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti if (systemEngine) { params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); // "01h11x0zmecanh1vp2q1mar5nh" } else { - // System engines do not support the following query params params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); params.put(FireboltQueryParameterKey.QUERY_ID.getKey(), statementInfoWrapper.getId()); params.put(FireboltQueryParameterKey.COMPRESS.getKey(), fireboltProperties.isCompress() ? "1" : "0"); diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 26acdcd3d..73e2a8975 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -78,7 +78,7 @@ public FireboltConnection(@NonNull String url, Properties connectionSettings, this.connect(); } - // This ugly code duplication between constructors is done because of back reference: dependent services require reference to current instance of FireboltConnection that prevents using constructor chaining or factory method. + // This code duplication between constructors is done because of back reference: dependent services require reference to current instance of FireboltConnection that prevents using constructor chaining or factory method. @ExcludeFromJacocoGeneratedReport public FireboltConnection(@NonNull String url, Properties connectionSettings) throws FireboltException { this.loginProperties = extractFireboltProperties(url, connectionSettings); @@ -86,16 +86,16 @@ public FireboltConnection(@NonNull String url, Properties connectionSettings) th ObjectMapper objectMapper = FireboltObjectMapper.getInstance(); this.fireboltAuthenticationService = new FireboltAuthenticationService(new FireboltAuthenticationClient(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); - this.fireboltGatewayUrlService = new FireboltGatewayUrlService(new FireboltAccountRetriever<>(httpClient, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), objectMapper, "engineUrl", GatewayUrlResponse.class)); + this.fireboltGatewayUrlService = new FireboltGatewayUrlService(createFireboltAccountRetriever(httpClient, objectMapper, "engineUrl", GatewayUrlResponse.class)); this.httpConnectionUrl = getHttpConnectionUrl(loginProperties); - this.fireboltStatementService = new FireboltStatementService(new StatementClientImpl(httpClient, this, objectMapper, loginProperties.getUserDrivers(), loginProperties.getUserClients())); + this.fireboltStatementService = new FireboltStatementService(new StatementClientImpl(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); this.statements = new ArrayList<>(); this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); this.systemEngine = loginProperties.isSystemEngine(); this.fireboltEngineService = new FireboltEngineService(this); - this.fireboltAccountIdService = new FireboltAccountIdService(new FireboltAccountRetriever<>(httpClient, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), objectMapper, "resolve", FireboltAccount.class)); + this.fireboltAccountIdService = new FireboltAccountIdService(createFireboltAccountRetriever(httpClient, objectMapper, "resolve", FireboltAccount.class)); this.connect(); } @@ -108,6 +108,10 @@ private static OkHttpClient getHttpClient(FireboltProperties fireboltProperties) } } + private <T> FireboltAccountRetriever<T> createFireboltAccountRetriever(OkHttpClient httpClient, ObjectMapper objectMapper, String path, Class<T> type) { + return new FireboltAccountRetriever<>(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), loginProperties.getHost(), path, type); + } + private void connect() throws FireboltException { String accessToken = this.getAccessToken(loginProperties).orElse(StringUtils.EMPTY); closed = false; diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index 8f523a117..7023d271d 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -11,6 +11,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; @Value @Builder(toBuilder = true) @@ -87,11 +88,12 @@ public static FireboltProperties of(Properties... properties) { int tcpKeepIdle = getSetting(mergedProperties, FireboltSessionProperty.TCP_KEEP_IDLE); int tcpKeepCount = getSetting(mergedProperties, FireboltSessionProperty.TCP_KEEP_COUNT); boolean logResultSet = getSetting(mergedProperties, FireboltSessionProperty.LOG_RESULT_SET); - String environment = getSetting(mergedProperties, FireboltSessionProperty.ENVIRONMENT); + String configuredEnvironment = getSetting(mergedProperties, FireboltSessionProperty.ENVIRONMENT); String driverVersions = getSetting(mergedProperties, FireboltSessionProperty.USER_DRIVERS); String clientVersions = getSetting(mergedProperties, FireboltSessionProperty.USER_CLIENTS); - String host = getHost(environment, mergedProperties); + String environment = getEnvironment(configuredEnvironment, mergedProperties); + String host = getHost(configuredEnvironment, mergedProperties); Integer port = getPort(mergedProperties, ssl); Map<String, String> additionalProperties = getFireboltCustomProperties(mergedProperties); @@ -127,6 +129,22 @@ private static String getHost(String environment, Properties properties ) { } } + private static String getEnvironment(String environment, Properties properties ) { + Pattern environmentalHost = Pattern.compile("api\\.(.+?)\\.firebolt\\.io"); + if (Objects.equals(environment, FireboltSessionProperty.ENVIRONMENT.getDefaultValue())) { + if (Stream.concat(Stream.of(FireboltSessionProperty.ENVIRONMENT.getKey()), Stream.of(FireboltSessionProperty.ENVIRONMENT.getAliases())).noneMatch(properties::containsKey)) { + String host = getSetting(properties, FireboltSessionProperty.HOST); + if (host != null) { + Matcher m = environmentalHost.matcher(host); + if (m.find() && m.group(1) != null) { + return m.group(1); + } + } + } + } + return environment; + } + @NonNull private static Integer getPort(Properties properties, boolean ssl) { Integer port = getSetting(properties, FireboltSessionProperty.PORT); diff --git a/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java index 4fcb1639c..d00021b4b 100644 --- a/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java +++ b/src/test/java/com/firebolt/jdbc/client/gateway/FireboltGatewayUrlClientTest.java @@ -43,8 +43,8 @@ class FireboltAccountRetrieverTest { @BeforeEach void setUp() { - fireboltGatewayUrlClient = new FireboltAccountRetriever<>(httpClient, fireboltConnection, null, null, objectMapper, "engineUrl", GatewayUrlResponse.class); - fireboltAccountIdResolver = new FireboltAccountRetriever<>(httpClient, fireboltConnection, null, null, objectMapper, "resolve", FireboltAccount.class); + fireboltGatewayUrlClient = new FireboltAccountRetriever<>(httpClient, objectMapper, fireboltConnection, null, null, "http://test-firebolt.io", "engineUrl", GatewayUrlResponse.class); + fireboltAccountIdResolver = new FireboltAccountRetriever<>(httpClient, objectMapper, fireboltConnection, null, null, "http://test-firebolt.io", "resolve", FireboltAccount.class); } @Test diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index 425080211..3fba163c9 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -50,7 +50,7 @@ void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { .thenReturn(Optional.of("token")); injectMockedResponse(okHttpClient, 200); - StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), + StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, "ConnA:1.0.9", "ConnB:2.0.9"); Call call = getMockedCallWithResponse(200); when(okHttpClient.newCall(any())).thenReturn(call); @@ -77,7 +77,7 @@ void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, .host("firebolt1").port(555).accountId("12345").build(); when(connection.getAccessToken()) .thenReturn(Optional.of("token")); - StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), + StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, "ConnA:1.0.9", "ConnB:2.0.9"); injectMockedResponse(okHttpClient, 200); Call call = getMockedCallWithResponse(200); @@ -98,8 +98,8 @@ void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, void shouldCancelSqlQuery() throws FireboltException, IOException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db1").compress(true) .host("firebolt1").port(555).build(); - StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, - mock(ObjectMapper.class), "", ""); + StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, + "", ""); injectMockedResponse(okHttpClient, 200); Call call = getMockedCallWithResponse(200); when(okHttpClient.newCall(any())).thenReturn(call); @@ -118,7 +118,7 @@ void shouldRetryOnUnauthorized() throws IOException, FireboltException { Call okCall = getMockedCallWithResponse(200); Call unauthorizedCall = getMockedCallWithResponse(401); when(okHttpClient.newCall(any())).thenReturn(unauthorizedCall).thenReturn(okCall); - StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), + StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, "ConnA:1.0.9", "ConnB:2.0.9"); StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("show databases").get(0); statementClient.executeSqlStatement(statementInfoWrapper, fireboltProperties, false, 5, 5, true); @@ -135,7 +135,7 @@ void shouldNotRetryNoMoreThanOnceOnUnauthorized() throws IOException, FireboltEx Call okCall = getMockedCallWithResponse(200); Call unauthorizedCall = getMockedCallWithResponse(401); when(okHttpClient.newCall(any())).thenReturn(unauthorizedCall).thenReturn(unauthorizedCall).thenReturn(okCall); - StatementClient statementClient = new StatementClientImpl(okHttpClient, connection, mock(ObjectMapper.class), + StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, "ConnA:1.0.9", "ConnB:2.0.9"); StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("show databases").get(0); FireboltException ex = assertThrows(FireboltException.class, () -> statementClient.executeSqlStatement(statementInfoWrapper, fireboltProperties, false, 5, 5, true)); diff --git a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java index 7b2875c6a..9610bf9f3 100644 --- a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java @@ -7,6 +7,8 @@ import java.util.Properties; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class FireboltPropertiesTest { @@ -152,4 +154,26 @@ void shouldSupportUserClientsAndDrivers() { assertEquals(drivers, FireboltProperties.of(properties).getUserDrivers()); } + @ParameterizedTest + @CsvSource(value = { + "env, qa,,api.qa.firebolt.io,qa", + "environment, test,,api.test.firebolt.io,test", + "env, staging,super-host.com,super-host.com,staging", + "env,,my-host.com,my-host.com,app", + "env,,api.dev.firebolt.io,api.dev.firebolt.io,dev", + "env,,something.io,something.io,app", // not standard host, no configured environment -> default environment + ",,,api.app.firebolt.io,app", // no host, no environment -> default environment (app) and default host api.app.firebolt.io + }, delimiter = ',') + void hostAndEnvironment(String envKey, String envValue, String host, String expectedHost, String expectedEnvironment) { + Properties properties = new Properties(); + if (envValue != null) { + properties.put(envKey, envValue); + } + if (host != null) { + properties.put("host", host); + } + assertEquals(expectedHost, FireboltProperties.of(properties).getHost()); + assertEquals(expectedEnvironment, FireboltProperties.of(properties).getEnvironment()); + } + } From f15c62a55d862bb1c18b8f0e17e500c5ebc45dbd Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Sun, 9 Jul 2023 17:57:57 +0300 Subject: [PATCH 04/15] fixed command line of integration tests --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 5f4d34bed..435b29282 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -73,7 +73,7 @@ jobs: fi - name: Run integration tests - run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }} -Daccount=${{ github.event.inputs.account }}" -Dengine=${{ github.event.inputs.engine }}" + run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }} -Daccount="${{ github.event.inputs.account }}" -Dengine="${{ github.event.inputs.engine }}" - name: "Foresight: Analyze Test Results" uses: runforesight/foresight-test-kit-action@v1 From 8859c86e78faf3c5268f4f6d9d29172c76e10b8d Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Sun, 9 Jul 2023 18:03:34 +0300 Subject: [PATCH 05/15] fixed command line of integration tests --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 435b29282..d6148db1e 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -73,7 +73,7 @@ jobs: fi - name: Run integration tests - run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }} -Daccount="${{ github.event.inputs.account }}" -Dengine="${{ github.event.inputs.engine }}" + run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }}" -Daccount="${{ github.event.inputs.account }}" -Dengine="${{ github.event.inputs.engine }}" - name: "Foresight: Analyze Test Results" uses: runforesight/foresight-test-kit-action@v1 From 08f6efa7955859aa1d356f11dda58b653d04a720 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Sun, 9 Jul 2023 19:10:24 +0300 Subject: [PATCH 06/15] removed test that has been already move to another testcase --- .../java/integration/tests/TimestampTest.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/integrationTest/java/integration/tests/TimestampTest.java b/src/integrationTest/java/integration/tests/TimestampTest.java index a62b8bb6e..ee3f671f7 100644 --- a/src/integrationTest/java/integration/tests/TimestampTest.java +++ b/src/integrationTest/java/integration/tests/TimestampTest.java @@ -131,18 +131,6 @@ void shouldRemoveOffsetDIffWhenTimestampOffsetHasChangedCET() throws SQLExceptio } } - @Test - void shouldHaveCorrectInfo() throws SQLException { - try (Connection connection = this.createConnection("system"); - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery("SELECT 3::decimal")) { - resultSet.next(); - assertEquals(9, resultSet.getMetaData().getScale(1)); - assertEquals(38, resultSet.getMetaData().getPrecision(1)); - } - - } - @Test void shouldReturnTimestampFromTimestampntz() throws SQLException { try (Connection connection = this.createConnection(); From cc1c7f60d07976147b746d1e223a2ebd23d3a240 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Mon, 10 Jul 2023 19:30:55 +0300 Subject: [PATCH 07/15] applied PR comments --- .../java/integration/ConnectionInfo.java | 1 + .../jdbc/connection/FireboltConnection.java | 2 +- .../settings/FireboltProperties.java | 49 ++++++++++------- .../settings/FireboltSessionProperty.java | 6 ++- .../jdbc/service/FireboltEngineService.java | 2 +- .../connection/FireboltConnectionTest.java | 12 +++++ .../settings/FireboltPropertiesTest.java | 34 +++++++++--- .../service/FireboltEngineServiceTest.java | 53 +++++++++---------- 8 files changed, 104 insertions(+), 55 deletions(-) diff --git a/src/integrationTest/java/integration/ConnectionInfo.java b/src/integrationTest/java/integration/ConnectionInfo.java index d9d527eeb..5e88373b5 100644 --- a/src/integrationTest/java/integration/ConnectionInfo.java +++ b/src/integrationTest/java/integration/ConnectionInfo.java @@ -6,6 +6,7 @@ public class ConnectionInfo { private static volatile ConnectionInfo INSTANCE; + // principal and secret are used here instead of client_id and client_secret respectively as more common term also used in java security API. private final String principal; private final String secret; private final String env; diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 73e2a8975..a7c7c56e8 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -117,7 +117,7 @@ private void connect() throws FireboltException { closed = false; if (!PropertyUtil.isLocalDb(loginProperties)) { internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, loginProperties.getAccount()); - String accountId = fireboltAccountIdService.getValue(accessToken, loginProperties.getAccount()); + String accountId = fireboltAccountIdService.getValue(accessToken, loginProperties.getAccount()); if (!loginProperties.isSystemEngine()) { sessionProperties = internalSystemEngineProperties.toBuilder() .engine(loginProperties.getEngine()) diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index 7023d271d..b79e4eeab 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -6,6 +6,7 @@ import lombok.Value; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; import java.util.*; import java.util.regex.Matcher; @@ -13,6 +14,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.lang.String.format; + @Value @Builder(toBuilder = true) @CustomLog @@ -56,7 +59,6 @@ public class FireboltProperties { Integer tcpKeepInterval; boolean logResultSet; boolean systemEngine; - String organization; String environment; String userDrivers; String userClients; @@ -123,23 +125,39 @@ private static String getEngine(Properties mergedProperties, String database) { private static String getHost(String environment, Properties properties ) { String host = getSetting(properties, FireboltSessionProperty.HOST); if (StringUtils.isEmpty(host)) { - return String.format("api.%s.firebolt.io", environment); + return format("api.%s.firebolt.io", environment); } else { return host; } } - private static String getEnvironment(String environment, Properties properties ) { + /** + * Discovers environment name from host if it matches pattern {@code api.ENV.firebolt.io} + * @param environment - the environment from properties or default value as defined in {@link FireboltSessionProperty#ENVIRONMENT} + * @param properties - configuration properties + * @return the environment value + * @throws IllegalStateException if environment extracted from host is not equal to given one. + */ + private static String getEnvironment(String environment, @NotNull Properties properties) { Pattern environmentalHost = Pattern.compile("api\\.(.+?)\\.firebolt\\.io"); - if (Objects.equals(environment, FireboltSessionProperty.ENVIRONMENT.getDefaultValue())) { - if (Stream.concat(Stream.of(FireboltSessionProperty.ENVIRONMENT.getKey()), Stream.of(FireboltSessionProperty.ENVIRONMENT.getAliases())).noneMatch(properties::containsKey)) { - String host = getSetting(properties, FireboltSessionProperty.HOST); - if (host != null) { - Matcher m = environmentalHost.matcher(host); - if (m.find() && m.group(1) != null) { - return m.group(1); - } - } + String envFromProps = Stream.concat(Stream.of(FireboltSessionProperty.ENVIRONMENT.getKey()), Stream.of(FireboltSessionProperty.ENVIRONMENT.getAliases())) + .map(properties::getProperty) + .filter(Objects::nonNull).findFirst() + .orElse(null); + String envFromHost = null; + String host = getSetting(properties, FireboltSessionProperty.HOST); + if (host != null) { + Matcher m = environmentalHost.matcher(host); + if (m.find() && m.group(1) != null) { + envFromHost = m.group(1); + } + } + if (envFromHost != null) { + if (envFromProps == null) { + return envFromHost; + } + if (!Objects.equals(environment, envFromHost)) { + throw new IllegalStateException(format("Environment %s does not match host %s", environment, host)); } } return environment; @@ -164,7 +182,7 @@ private static String getDatabase(Properties properties, String path) throws Ill if (m.matches()) { return m.group(1); } else { - throw new IllegalArgumentException(String.format("The database provided is invalid %s", path)); + throw new IllegalArgumentException(format("The database provided is invalid %s", path)); } } } else { @@ -233,9 +251,4 @@ public void addProperty(@NonNull String key, String value) { public void addProperty(Pair<String, String> property) { this.addProperty(property.getLeft(), property.getRight()); } - - public static FireboltProperties toSystemEngineProperties(FireboltProperties properties) { - return properties.toBuilder().additionalProperties(new HashMap<>(properties.getAdditionalProperties())).build(); - } - } diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java index 3690f90f4..08212a3ba 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltSessionProperty.java @@ -44,8 +44,10 @@ public enum FireboltSessionProperty { */ "compress", true, Boolean.class, "Whether to compress transferred data or not. Compressed by default"), DATABASE("database", null, String.class, "default database name"), - CLIENT_SECRET("client_secret", null, String.class, "user password - null by default", "password"), - CLIENT_ID("client_id", null, String.class, "user name - null by default", "user"), + // Typically client_secret property should be used, but password is the standard JDBC property supported by all tools, so it is silently defined here as alias. Also see CLIENT_ID. + CLIENT_SECRET("client_secret", null, String.class, "client secret - null by default", "password"), + // Typically client_id property should be used, but user is the standard JDBC property supported by all tools, so it is silently defined here as alias. Also see CLIENT_SECRET + CLIENT_ID("client_id", null, String.class, "client ID - null by default", "user"), HOST("host", null, String.class, "Firebolt host - null by default"), PORT("port", null, Integer.class, "Firebolt port - null by default"), ENGINE("engine", null, String.class, "engine - null by default", "engine_name"), diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java index ed562548d..ee009cc9c 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java @@ -49,7 +49,7 @@ public String getEngineNameByHost(String engineHost) throws FireboltException { public Engine getEngine(@Nullable String name, @Nullable String database) throws FireboltException { if (StringUtils.isEmpty(name)) { - return this.getDefaultEngine(database); + return getDefaultEngine(database); } else { return Engine.builder().name(name).endpoint(getEngineEndpoint(name)).build(); } diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index 509ef5f26..737aed831 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -409,6 +409,18 @@ void shouldNotGetEngineUrlOrDefaultEngineUrlWhenUsingSystemEngine() throws SQLEx } } + @Test + void noEngineAndDb() throws SQLException { + when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://my_endpoint"); + + try (FireboltConnection connection = createConnection("jdbc:firebolt:?env=dev", connectionProperties)) { + assertEquals("my_endpoint", connection.getSessionProperties().getHost()); + assertEquals("system", connection.getSessionProperties().getEngine()); + assertTrue(connection.getSessionProperties().isSystemEngine()); + } + } + + private FireboltConnection createConnection(String url, Properties props) throws FireboltException { return new FireboltConnection(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); } diff --git a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java index 9610bf9f3..eefa17ef9 100644 --- a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java @@ -1,14 +1,14 @@ package com.firebolt.jdbc.connection.settings; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.util.HashMap; import java.util.Map; import java.util.Properties; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.*; class FireboltPropertiesTest { @@ -154,6 +154,11 @@ void shouldSupportUserClientsAndDrivers() { assertEquals(drivers, FireboltProperties.of(properties).getUserDrivers()); } + @Test + void noEngineNoDbSystemEngine() { + assertEquals("system", FireboltProperties.of(new Properties()).getEngine()); + } + @ParameterizedTest @CsvSource(value = { "env, qa,,api.qa.firebolt.io,qa", @@ -163,8 +168,26 @@ void shouldSupportUserClientsAndDrivers() { "env,,api.dev.firebolt.io,api.dev.firebolt.io,dev", "env,,something.io,something.io,app", // not standard host, no configured environment -> default environment ",,,api.app.firebolt.io,app", // no host, no environment -> default environment (app) and default host api.app.firebolt.io + ",,api.app.firebolt.io,api.app.firebolt.io,app", // no configured environment, discover default environment from host + ",,api.dev.firebolt.io,api.dev.firebolt.io,dev", // no configured environment, discover not default environment from host }, delimiter = ',') void hostAndEnvironment(String envKey, String envValue, String host, String expectedHost, String expectedEnvironment) { + Properties properties = properties(envKey, envValue, host); + assertEquals(expectedHost, FireboltProperties.of(properties).getHost()); + assertEquals(expectedEnvironment, FireboltProperties.of(properties).getEnvironment()); + } + + @ParameterizedTest + @CsvSource(value = { + "env,app,api.dev.firebolt.io", + "env,qa,api.app.firebolt.io", + }, delimiter = ',') + void environmentDoesNotMatch(String envKey, String envValue, String host) { + Properties properties = properties(envKey, envValue, host); + assertThrows(IllegalStateException.class, () -> FireboltProperties.of(properties)); + } + + private Properties properties(String envKey, String envValue, String host) { Properties properties = new Properties(); if (envValue != null) { properties.put(envKey, envValue); @@ -172,8 +195,7 @@ void hostAndEnvironment(String envKey, String envValue, String host, String expe if (host != null) { properties.put("host", host); } - assertEquals(expectedHost, FireboltProperties.of(properties).getHost()); - assertEquals(expectedEnvironment, FireboltProperties.of(properties).getEnvironment()); + return properties; } } diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java index 412ee2daa..6bca42345 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java @@ -5,13 +5,19 @@ import com.firebolt.jdbc.exception.FireboltException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -46,50 +52,43 @@ void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromNullHost() { @Test void shouldGetDefaultEngineWhenEngineNameIsNotProvided() throws SQLException { PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(true); - when(resultSet.getString("status")).thenReturn("running"); - when(resultSet.getString("url")).thenReturn("https://url"); - when(resultSet.getString("engine_name")).thenReturn("hello-engine"); + ResultSet resultSet = mockedResultSet(Map.of("status", "running", "url", "https://url", "engine_name", "hello-engine")); when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); when(statement.executeQuery()).thenReturn(resultSet); assertEquals(Engine.builder().endpoint("https://url").name("hello-engine").build(), fireboltEngineService.getEngine(null, "db")); } - @Test - void shouldThrowExceptionWhenDefaultEngineNotRunning() throws SQLException { - PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(true); - when(resultSet.getString("status")).thenReturn("down"); - when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); - when(statement.executeQuery()).thenReturn(resultSet); - assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(null, "db")); - } - @Test void shouldGetEngineWhenEngineNameIsProvided() throws SQLException { PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(true); - when(resultSet.getString("status")).thenReturn("running"); - when(resultSet.getString("url")).thenReturn("https://url"); + ResultSet resultSet = mockedResultSet(Map.of("status", "running", "url", "https://url")); when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); when(statement.executeQuery()).thenReturn(resultSet); assertEquals(Engine.builder().endpoint("https://url").name("some-engine").build(), fireboltEngineService.getEngine("some-engine", "db")); } - @Test - void shouldThrowExceptionWhenEngineNotRunning() throws SQLException { + @ParameterizedTest + @CsvSource(value = { + "down;some-engine;db;SELECT url, status", + "down;;db;SELECT.+JOIN", + "failed;'';db;SELECT.+JOIN", + }, delimiter = ';') + void shouldThrowExceptionWhenEngineNotRunning(String status, String engineName, String db, String queryRegex) throws SQLException { PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(true); - when(resultSet.getString("status")).thenReturn("down"); - when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); + ResultSet resultSet = mockedResultSet(Map.of("status", status)); + when(fireboltConnection.prepareStatement(Mockito.matches(Pattern.compile(queryRegex, Pattern.MULTILINE | Pattern.DOTALL)))).thenReturn(statement); when(statement.executeQuery()).thenReturn(resultSet); - assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine("some-engine", "db")); + assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(engineName, db)); } + private ResultSet mockedResultSet(Map<String, String> values) throws SQLException { + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(true); + for (Entry<String, String> column : values.entrySet()) { + when(resultSet.getString(column.getKey())).thenReturn(column.getValue()); + } + return resultSet; + } } From 675f8d062ee31e9922e359c3318dfa9a45ce199b Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Mon, 10 Jul 2023 19:32:50 +0300 Subject: [PATCH 08/15] applied PR comments --- .../com/firebolt/jdbc/client/query/StatementClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index 83a8ac740..1f7b08805 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -205,7 +205,7 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti getResponseFormatParameter(statementInfoWrapper.getType() == StatementType.QUERY, isLocalDb) .ifPresent(format -> params.put(format.getLeft(), format.getRight())); if (systemEngine) { - params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); // "01h11x0zmecanh1vp2q1mar5nh" + params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); } else { params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); params.put(FireboltQueryParameterKey.QUERY_ID.getKey(), statementInfoWrapper.getId()); From 2aee7a2dbadb29da3f9450301f1b7a7c1825dd2f Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Tue, 11 Jul 2023 14:46:37 +0300 Subject: [PATCH 09/15] applied some pr comments; TimeoutTest fails --- .../integration/tests/NumericTypesTest.java | 2 +- .../integration/tests/SystemEngineTest.java | 9 ++-- .../client/query/StatementClientImpl.java | 26 +++++++---- .../jdbc/connection/FireboltConnection.java | 13 ++---- .../settings/FireboltProperties.java | 9 +--- .../connection/FireboltConnectionTest.java | 8 ++-- .../settings/FireboltPropertiesTest.java | 46 ++++--------------- 7 files changed, 41 insertions(+), 72 deletions(-) diff --git a/src/integrationTest/java/integration/tests/NumericTypesTest.java b/src/integrationTest/java/integration/tests/NumericTypesTest.java index c0c2dda5b..5badb28b6 100644 --- a/src/integrationTest/java/integration/tests/NumericTypesTest.java +++ b/src/integrationTest/java/integration/tests/NumericTypesTest.java @@ -13,7 +13,7 @@ public class NumericTypesTest extends IntegrationTest { @Test void shouldHaveCorrectInfo() throws SQLException { - try (Connection connection = this.createConnection("system"); + try (Connection connection = this.createConnection(null); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT 3::decimal")) { resultSet.next(); diff --git a/src/integrationTest/java/integration/tests/SystemEngineTest.java b/src/integrationTest/java/integration/tests/SystemEngineTest.java index cd596d09d..eb348e22e 100644 --- a/src/integrationTest/java/integration/tests/SystemEngineTest.java +++ b/src/integrationTest/java/integration/tests/SystemEngineTest.java @@ -18,12 +18,11 @@ public class SystemEngineTest extends IntegrationTest { private static final String DATABASE_NAME = "jdbc_system_engine_integration_test"; private static final String ENGINE_NAME = "jdbc_system_engine_integration_test_engine"; private static final String ENGINE_NEW_NAME = "jdbc_system_engine_integration_test_engine_2"; - private static final String SYSTEM_ENGINE_NAME = "system"; @BeforeAll void beforeAll() { try { - executeStatementFromFile("/statements/system/ddl.sql", SYSTEM_ENGINE_NAME); + executeStatementFromFile("/statements/system/ddl.sql", null); } catch (Exception e) { log.warn("Could not execute statement", e); } @@ -32,15 +31,15 @@ void beforeAll() { @AfterAll void afterAll() { try { - executeStatementFromFile("/statements/system/cleanup.sql", SYSTEM_ENGINE_NAME); + executeStatementFromFile("/statements/system/cleanup.sql", null); } catch (Exception e) { log.warn("Could not execute statement", e); } } @Test - void shouldExecuteQueriesUsingSystemEngine() throws SQLException { - try (Connection connection = this.createConnection(SYSTEM_ENGINE_NAME)) { + void shouldExecuteEngineManagementQueries() throws SQLException { + try (Connection connection = this.createConnection(null)) { List<String> queries = Arrays.asList(String.format("CREATE DATABASE IF NOT EXISTS %s", DATABASE_NAME), String.format("CREATE ENGINE %s", ENGINE_NAME), String.format("ATTACH ENGINE %s TO %s;", ENGINE_NAME, DATABASE_NAME), diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index 1f7b08805..cbcec6eb3 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -207,17 +207,27 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti if (systemEngine) { params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); } else { - params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); +// params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); params.put(FireboltQueryParameterKey.QUERY_ID.getKey(), statementInfoWrapper.getId()); params.put(FireboltQueryParameterKey.COMPRESS.getKey(), fireboltProperties.isCompress() ? "1" : "0"); +// +// if (queryTimeout > -1) { +// params.put("max_execution_time", String.valueOf(queryTimeout)); +// } +// if (maxRows > 0) { +// params.put("max_result_rows", String.valueOf(maxRows)); +// params.put("result_overflow_mode", "break"); +// } + } - if (queryTimeout > -1) { - params.put("max_execution_time", String.valueOf(queryTimeout)); - } - if (maxRows > 0) { - params.put("max_result_rows", String.valueOf(maxRows)); - params.put("result_overflow_mode", "break"); - } + params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); + + if (queryTimeout > -1) { + params.put("max_execution_time", String.valueOf(queryTimeout)); + } + if (maxRows > 0) { + params.put("max_result_rows", String.valueOf(maxRows)); + params.put("resulresult_overflow_modet_overflow_mode", "break"); } return params; diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index a7c7c56e8..722f2c3fe 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -33,7 +33,6 @@ import java.util.*; import java.util.concurrent.Executor; -import static com.firebolt.jdbc.connection.settings.FireboltProperties.SYSTEM_ENGINE_NAME; import static java.sql.ResultSet.TYPE_FORWARD_ONLY; @CustomLog @@ -50,7 +49,6 @@ public class FireboltConnection implements Connection { private final boolean systemEngine; private boolean closed = true; private FireboltProperties sessionProperties; - private FireboltProperties internalSystemEngineProperties; private int networkTimeout; //Properties that are used at the beginning of the connection for authentication @@ -116,18 +114,18 @@ private void connect() throws FireboltException { String accessToken = this.getAccessToken(loginProperties).orElse(StringUtils.EMPTY); closed = false; if (!PropertyUtil.isLocalDb(loginProperties)) { - internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, loginProperties.getAccount()); + FireboltProperties internalSystemEngineProperties = createInternalSystemEngineProperties(accessToken, loginProperties.getAccount()); String accountId = fireboltAccountIdService.getValue(accessToken, loginProperties.getAccount()); - if (!loginProperties.isSystemEngine()) { + if (systemEngine) { + //When using system engine, the system engine properties are the same as the session properties + sessionProperties = internalSystemEngineProperties.toBuilder().accountId(accountId).build(); + } else { sessionProperties = internalSystemEngineProperties.toBuilder() .engine(loginProperties.getEngine()) .systemEngine(true) .accountId(accountId) .build(); sessionProperties = getSessionPropertiesForNonSystemEngine(); - } else { - //When using system engine, the system engine properties are the same as the session properties - sessionProperties = internalSystemEngineProperties.toBuilder().accountId(accountId).build(); } } else { //When running packdb locally, the login properties are the session properties @@ -146,7 +144,6 @@ private FireboltProperties createInternalSystemEngineProperties(String accessTok return this.loginProperties .toBuilder() .systemEngine(true) - .engine(SYSTEM_ENGINE_NAME) .compress(false) .host(UrlUtil.createUrl(systemEngineEndpoint).getHost()).database(null).build(); } diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index b79e4eeab..f13bc33fd 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -21,7 +21,6 @@ @CustomLog public class FireboltProperties { - public static final String SYSTEM_ENGINE_NAME = "system"; private static final Pattern DB_PATH_PATTERN = Pattern.compile("([a-zA-Z0-9_*\\-]+)"); private static final int FIREBOLT_SSL_PROXY_PORT = 443; private static final int FIREBOLT_NO_SSL_PROXY_PORT = 9090; @@ -115,11 +114,7 @@ public static FireboltProperties of(Properties... properties) { private static String getEngine(Properties mergedProperties, String database) { String engine = getSetting(mergedProperties, FireboltSessionProperty.ENGINE); - if (StringUtils.isEmpty(engine) && StringUtils.isEmpty(database)) { - return SYSTEM_ENGINE_NAME; - } else { - return engine; - } + return StringUtils.isEmpty(engine) && StringUtils.isEmpty(database) ? null : engine; } private static String getHost(String environment, Properties properties ) { @@ -241,7 +236,7 @@ public static FireboltProperties copy(FireboltProperties properties) { } private static boolean isSystemEngine(String engine) { - return StringUtils.equalsIgnoreCase(SYSTEM_ENGINE_NAME, engine); + return engine == null; } public void addProperty(@NonNull String key, String value) { diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index 737aed831..014f7626e 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -22,8 +22,7 @@ import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -399,12 +398,11 @@ void shouldGetEngineUrlWhenEngineIsProvided() throws SQLException { @Test void shouldNotGetEngineUrlOrDefaultEngineUrlWhenUsingSystemEngine() throws SQLException { - connectionProperties.put("engine", "system"); connectionProperties.put("database", "my_db"); when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://my_endpoint"); try (FireboltConnection connection = createConnection(URL, connectionProperties)) { - verify(fireboltEngineService, times(0)).getEngine(eq("system"), eq("my_db")); + verify(fireboltEngineService, times(0)).getEngine(isNull(), eq("my_db")); assertEquals("my_endpoint", connection.getSessionProperties().getHost()); } } @@ -415,7 +413,7 @@ void noEngineAndDb() throws SQLException { try (FireboltConnection connection = createConnection("jdbc:firebolt:?env=dev", connectionProperties)) { assertEquals("my_endpoint", connection.getSessionProperties().getHost()); - assertEquals("system", connection.getSessionProperties().getEngine()); + assertNull(connection.getSessionProperties().getEngine()); assertTrue(connection.getSessionProperties().isSystemEngine()); } } diff --git a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java index eefa17ef9..d29d53b80 100644 --- a/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/settings/FireboltPropertiesTest.java @@ -1,5 +1,6 @@ package com.firebolt.jdbc.connection.settings; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -14,7 +15,7 @@ class FireboltPropertiesTest { @Test void shouldHaveDefaultPropertiesWhenOnlyTheRequiredFieldsAreSpecified() { - FireboltProperties expectedDefaultProperties = FireboltProperties.builder().database("db").bufferSize(65536) + FireboltProperties expectedDefaultProperties = FireboltProperties.builder().engine("engine").database("db").bufferSize(65536) .sslCertificatePath("").sslMode("strict").path("").port(443) // 443 by default as SSL is enabled by .systemEngine(false).compress(true) // default .principal(null).secret(null).host("host").ssl(true).additionalProperties(new HashMap<>()) @@ -23,6 +24,7 @@ void shouldHaveDefaultPropertiesWhenOnlyTheRequiredFieldsAreSpecified() { .tcpKeepCount(10).account("firebolt").build(); Properties properties = new Properties(); + properties.put("engine", "engine"); properties.put("host", "host"); properties.put("database", "db"); assertEquals(expectedDefaultProperties, FireboltProperties.of(properties)); @@ -31,6 +33,7 @@ void shouldHaveDefaultPropertiesWhenOnlyTheRequiredFieldsAreSpecified() { @Test void shouldHaveAllTheSpecifiedCustomProperties() { Properties properties = new Properties(); + properties.put("engine", "my_test"); properties.put("buffer_size", "51"); properties.put("socket_timeout_millis", "20"); properties.put("ssl", "1"); @@ -45,7 +48,7 @@ void shouldHaveAllTheSpecifiedCustomProperties() { Map<String, String> customProperties = new HashMap<>(); customProperties.put("someCustomProperties", "custom_value"); - FireboltProperties expectedDefaultProperties = FireboltProperties.builder().bufferSize(51) + FireboltProperties expectedDefaultProperties = FireboltProperties.builder().engine("my_test").bufferSize(51) .sslCertificatePath("root_cert").sslMode("none").path("example").database("myDb").compress(true) .port(443).principal(null).secret(null).host("myDummyHost").ssl(true).systemEngine(false) .additionalProperties(customProperties).account(null).keepAliveTimeoutMillis(300000) @@ -101,43 +104,10 @@ void shouldUseCustomPortWhenProvided() { void shouldUseSystemEngineWhenNoDbOrEngineProvided() { Properties properties = new Properties(); FireboltProperties fireboltProperties = FireboltProperties.of(properties); - assertTrue(FireboltProperties.of(properties).isSystemEngine()); - assertEquals("system", fireboltProperties.getEngine()); - assertNull(fireboltProperties.getDatabase()); - assertFalse(fireboltProperties.isCompress()); - } - - @Test - void shouldNotUseSystemEngineWhenDbAsPathIsProvided() { - Properties properties = new Properties(); - properties.put("path", "example"); - FireboltProperties fireboltProperties = FireboltProperties.of(properties); - assertFalse(FireboltProperties.of(properties).isSystemEngine()); - assertNull(fireboltProperties.getEngine()); - assertEquals("example", fireboltProperties.getDatabase()); - assertTrue(fireboltProperties.isCompress()); - } - - @Test - void shouldNotUseSystemEngineWhenDbAsQueryParamIsProvided() { - Properties properties = new Properties(); - properties.put("database", "example"); - FireboltProperties fireboltProperties = FireboltProperties.of(properties); - assertFalse(FireboltProperties.of(properties).isSystemEngine()); + assertTrue(fireboltProperties.isSystemEngine()); assertNull(fireboltProperties.getEngine()); - assertEquals("example", fireboltProperties.getDatabase()); - assertTrue(fireboltProperties.isCompress()); - } - - @Test - void shouldNotUseSystemEngineWhenEngineIsProvided() { - Properties properties = new Properties(); - properties.put("engine", "example"); - FireboltProperties fireboltProperties = FireboltProperties.of(properties); - assertFalse(FireboltProperties.of(properties).isSystemEngine()); assertNull(fireboltProperties.getDatabase()); - assertEquals("example", fireboltProperties.getEngine()); - assertTrue(fireboltProperties.isCompress()); + assertFalse(fireboltProperties.isCompress()); } @Test @@ -156,7 +126,7 @@ void shouldSupportUserClientsAndDrivers() { @Test void noEngineNoDbSystemEngine() { - assertEquals("system", FireboltProperties.of(new Properties()).getEngine()); + assertNull(FireboltProperties.of(new Properties()).getEngine()); } @ParameterizedTest From b254840e50171991ffebf2ea6608dd9755626277 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Wed, 12 Jul 2023 11:37:08 +0300 Subject: [PATCH 10/15] completely removed "system" for system engine --- .../java/integration/ConnectionInfo.java | 44 +++++++++++++-- .../java/integration/IntegrationTest.java | 35 +++++------- .../client/query/StatementClientImpl.java | 11 +--- .../settings/FireboltProperties.java | 7 +-- .../client/query/StatementClientImplTest.java | 55 ++++++++----------- .../connection/FireboltConnectionTest.java | 16 +----- 6 files changed, 81 insertions(+), 87 deletions(-) diff --git a/src/integrationTest/java/integration/ConnectionInfo.java b/src/integrationTest/java/integration/ConnectionInfo.java index 5e88373b5..8ce560933 100644 --- a/src/integrationTest/java/integration/ConnectionInfo.java +++ b/src/integrationTest/java/integration/ConnectionInfo.java @@ -1,10 +1,15 @@ package integration; +import java.util.Objects; import java.util.Optional; +import java.util.stream.Stream; +import static java.lang.String.format; import static java.lang.System.getProperty; +import static java.util.stream.Collectors.joining; public class ConnectionInfo { + private static final String JDBC_URL_PREFIX = "jdbc:firebolt:"; private static volatile ConnectionInfo INSTANCE; // principal and secret are used here instead of client_id and client_secret respectively as more common term also used in java security API. private final String principal; @@ -15,12 +20,23 @@ public class ConnectionInfo { private final String engine; private ConnectionInfo() { - principal = Optional.ofNullable(getProperty("client_id", getProperty("user"))).map(u -> u.replace("\"", "")).orElse(null); - secret = Optional.ofNullable(getProperty("client_secret", getProperty("password"))).map(p -> p.replace("\"", "")).orElse(null); - env = getProperty("env"); - database = getProperty("db"); - account = getProperty("account"); - engine = getProperty("engine"); + this( + getTrimmedProperty("client_id", "user"), + getTrimmedProperty("client_secret", "password"), + getProperty("env"), + getProperty("db"), + getProperty("account"), + getProperty("engine") + ); + } + + public ConnectionInfo(String principal, String secret, String env, String database, String account, String engine) { + this.principal = principal; + this.secret = secret; + this.env = env; + this.database = database; + this.account = account; + this.engine = engine; } public static ConnectionInfo getInstance() { @@ -34,6 +50,10 @@ public static ConnectionInfo getInstance() { return INSTANCE; } + private static String getTrimmedProperty(String name, String alias) { + return Optional.ofNullable(getProperty(name, getProperty(alias))).map(u -> u.replace("\"", "")).orElse(null); + } + public String getPrincipal() { return principal; } @@ -57,4 +77,16 @@ public String getAccount() { public String getEngine() { return engine; } + + public String toJdbcUrl() { + String params = Stream.of(param("env", env), param("engine", engine), param("account", account)).filter(Objects::nonNull).collect(joining("&")); + if (!params.isEmpty()) { + params = "?" + params; + } + return JDBC_URL_PREFIX + database + params; + } + + private String param(String name, String value) { + return value == null ? null : format("%s=%s", name, value); + } } diff --git a/src/integrationTest/java/integration/IntegrationTest.java b/src/integrationTest/java/integration/IntegrationTest.java index a66eba9d4..c8628f6b3 100644 --- a/src/integrationTest/java/integration/IntegrationTest.java +++ b/src/integrationTest/java/integration/IntegrationTest.java @@ -1,20 +1,20 @@ package integration; +import com.firebolt.jdbc.client.HttpClientConfig; +import com.google.common.io.Resources; +import lombok.CustomLog; +import lombok.SneakyThrows; +import org.junit.jupiter.api.TestInstance; + import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; -import java.util.Optional; - -import org.junit.jupiter.api.TestInstance; - -import com.firebolt.jdbc.client.HttpClientConfig; -import com.google.common.io.Resources; +import java.util.Objects; -import lombok.CustomLog; -import lombok.SneakyThrows; +import static java.util.Objects.requireNonNull; @CustomLog @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -31,18 +31,16 @@ protected Connection createLocalConnection(String queryParams) throws SQLExcepti } protected Connection createConnection() throws SQLException { - return DriverManager.getConnection( - JDBC_URL_PREFIX - + integration.ConnectionInfo.getInstance().getDatabase() + "?" + getEnvParam() + getAccountParam() , + return DriverManager.getConnection(integration.ConnectionInfo.getInstance().toJdbcUrl(), integration.ConnectionInfo.getInstance().getPrincipal(), integration.ConnectionInfo.getInstance().getSecret()); } protected Connection createConnection(String engine) throws SQLException { - return DriverManager.getConnection( - JDBC_URL_PREFIX + - integration.ConnectionInfo.getInstance().getDatabase() - + Optional.ofNullable(engine).map(e -> "?" + getEnvParam() +"&engine=" + e + getAccountParam() ).orElse("?" + getEnvParam() + getAccountParam()), + ConnectionInfo current = integration.ConnectionInfo.getInstance(); + ConnectionInfo updated = new ConnectionInfo(current.getPrincipal(), current.getSecret(), + current.getEnv(), current.getDatabase(), current.getAccount(), engine); + return DriverManager.getConnection(updated.toJdbcUrl(), integration.ConnectionInfo.getInstance().getPrincipal(), integration.ConnectionInfo.getInstance().getSecret()); } @@ -61,7 +59,7 @@ protected void executeStatementFromFile(String path) { @SneakyThrows protected void executeStatementFromFile(String path, String engine) { try (Connection connection = createConnection(engine); Statement statement = connection.createStatement()) { - String sql = Resources.toString(IntegrationTest.class.getResource(path), StandardCharsets.UTF_8); + String sql = Resources.toString(requireNonNull(IntegrationTest.class.getResource(path)), StandardCharsets.UTF_8); statement.execute(sql); } } @@ -75,9 +73,4 @@ protected void removeExistingClient() throws NoSuchFieldException, IllegalAccess private String getAccountParam() { return "&account=" + integration.ConnectionInfo.getInstance().getAccount(); } - - private String getEnvParam() { - return "&env=" + integration.ConnectionInfo.getInstance().getEnv(); - } - } diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index cbcec6eb3..af06cfc93 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -207,17 +207,8 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti if (systemEngine) { params.put(FireboltQueryParameterKey.ACCOUNT_ID.getKey(), fireboltProperties.getAccountId()); } else { -// params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); params.put(FireboltQueryParameterKey.QUERY_ID.getKey(), statementInfoWrapper.getId()); params.put(FireboltQueryParameterKey.COMPRESS.getKey(), fireboltProperties.isCompress() ? "1" : "0"); -// -// if (queryTimeout > -1) { -// params.put("max_execution_time", String.valueOf(queryTimeout)); -// } -// if (maxRows > 0) { -// params.put("max_result_rows", String.valueOf(maxRows)); -// params.put("result_overflow_mode", "break"); -// } } params.put(FireboltQueryParameterKey.DATABASE.getKey(), fireboltProperties.getDatabase()); @@ -227,7 +218,7 @@ private Map<String, String> getAllParameters(FireboltProperties fireboltProperti } if (maxRows > 0) { params.put("max_result_rows", String.valueOf(maxRows)); - params.put("resulresult_overflow_modet_overflow_mode", "break"); + params.put("result_overflow_mode", "break"); } return params; diff --git a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java index f13bc33fd..16535e3bd 100644 --- a/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java +++ b/src/main/java/com/firebolt/jdbc/connection/settings/FireboltProperties.java @@ -74,7 +74,7 @@ public static FireboltProperties of(Properties... properties) { String secret = getSetting(mergedProperties, FireboltSessionProperty.CLIENT_SECRET); String path = getSetting(mergedProperties, FireboltSessionProperty.PATH); String database = getDatabase(mergedProperties, path); - String engine = getEngine(mergedProperties, database); + String engine = getEngine(mergedProperties); boolean isSystemEngine = isSystemEngine(engine); boolean compress = ((Boolean) getSetting(mergedProperties, FireboltSessionProperty.COMPRESS)) && !isSystemEngine; @@ -112,9 +112,8 @@ public static FireboltProperties of(Properties... properties) { .build(); } - private static String getEngine(Properties mergedProperties, String database) { - String engine = getSetting(mergedProperties, FireboltSessionProperty.ENGINE); - return StringUtils.isEmpty(engine) && StringUtils.isEmpty(database) ? null : engine; + private static String getEngine(Properties mergedProperties) { + return getSetting(mergedProperties, FireboltSessionProperty.ENGINE); } private static String getHost(String environment, Properties properties ) { diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index 3fba163c9..f9f12943d 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -10,7 +10,6 @@ import lombok.NonNull; import okhttp3.*; import okio.Buffer; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -19,13 +18,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import static com.firebolt.jdbc.client.UserAgentFormatter.userAgent; +import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -42,20 +42,35 @@ class StatementClientImplTest { private FireboltConnection connection; @Test - @Disabled("Disabled until engine_url is available") void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { + Entry<String, String> result = shouldPostSqlQueryForSystemEngine(false); + String requestId = result.getKey(); + String url = result.getValue(); + assertEquals( + format("http://firebolt1:555/?result_overflow_mode=break&database=db1&output_format=TabSeparatedWithNamesAndTypes&query_id=%s&compress=1&max_result_rows=1&max_execution_time=15", requestId), + url); + } + + @Test + void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException { + String url = shouldPostSqlQueryForSystemEngine(true).getValue(); + assertEquals( + "http://firebolt1:555/dynamic/query?result_overflow_mode=break&database=db1&account_id=12345&output_format=TabSeparatedWithNamesAndTypes&max_result_rows=1&max_execution_time=15", + url); + } + + private Entry<String, String> shouldPostSqlQueryForSystemEngine(boolean systemEngine) throws FireboltException, IOException { FireboltProperties fireboltProperties = FireboltProperties.builder().database("db1").compress(true) - .host("firebolt1").port(555).build(); + .host("firebolt1").port(555).accountId("12345").systemEngine(systemEngine).build(); when(connection.getAccessToken()) .thenReturn(Optional.of("token")); - - injectMockedResponse(okHttpClient, 200); StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, "ConnA:1.0.9", "ConnB:2.0.9"); + injectMockedResponse(okHttpClient, 200); Call call = getMockedCallWithResponse(200); when(okHttpClient.newCall(any())).thenReturn(call); StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("show databases").get(0); - statementClient.executeSqlStatement(statementInfoWrapper, fireboltProperties, false, 15, 1, true); + statementClient.executeSqlStatement(statementInfoWrapper, fireboltProperties, fireboltProperties.isSystemEngine(), 15, 1, true); verify(okHttpClient).newCall(requestArgumentCaptor.capture()); Request actualRequest = requestArgumentCaptor.getValue(); @@ -65,33 +80,9 @@ void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { expectedHeaders.put("User-Agent", userAgent("ConnB/2.0.9 JDBC/%s (Java %s; %s %s; ) ConnA/1.0.9")); assertEquals(expectedHeaders, extractHeadersMap(actualRequest)); - assertEquals("show databases;", actualQuery); - assertEquals(String.format( - "http://firebolt1:555/dynamic/query?result_overflow_mode=break&database=db1&output_format=TabSeparatedWithNamesAndTypes&query_id=1cfd2cc6-3a62-48e2-ac9c-83846d70f16a&compress=1&max_result_rows=1&max_execution_time=15", - statementInfoWrapper.getId()), actualRequest.url().toString()); - } - - @Test - void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException, URISyntaxException { - FireboltProperties fireboltProperties = FireboltProperties.builder().database("db1").compress(true) - .host("firebolt1").port(555).accountId("12345").build(); - when(connection.getAccessToken()) - .thenReturn(Optional.of("token")); - StatementClient statementClient = new StatementClientImpl(okHttpClient, mock(ObjectMapper.class), connection, - "ConnA:1.0.9", "ConnB:2.0.9"); - injectMockedResponse(okHttpClient, 200); - Call call = getMockedCallWithResponse(200); - when(okHttpClient.newCall(any())).thenReturn(call); - StatementInfoWrapper statementInfoWrapper = StatementUtil.parseToStatementInfoWrappers("show databases").get(0); - statementClient.executeSqlStatement(statementInfoWrapper, fireboltProperties, true, 15, 1, true); - - verify(okHttpClient).newCall(requestArgumentCaptor.capture()); - Request actualRequest = requestArgumentCaptor.getValue(); - String actualQuery = getActualRequestString(actualRequest); assertEquals("show databases;", actualQuery); - assertEquals("http://firebolt1:555/?account_id=12345&output_format=TabSeparatedWithNamesAndTypes", - actualRequest.url().toString()); + return Map.entry(statementInfoWrapper.getId(), actualRequest.url().toString()); } @Test diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index 014f7626e..f225c9452 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -30,7 +30,7 @@ class FireboltConnectionTest { private static final String URL = "jdbc:firebolt:db?env=dev&engine=eng"; - private static final String SYSTEM_ENGINE_URL = "jdbc:firebolt:db?env=dev&engine=system"; + private static final String SYSTEM_ENGINE_URL = "jdbc:firebolt:db?env=dev"; private static final String LOCAL_URL = "jdbc:firebolt:local_dev_db?ssl=false&max_query_size=10000000&use_standard_sql=1&mask_internal_errors=0&firebolt_enable_beta_functions=1&firebolt_case_insensitive_identifiers=1&rest_api_pull_timeout_sec=3600&rest_api_pull_interval_millisec=5000&rest_api_retry_times=10&host=localhost"; private final FireboltConnectionTokens fireboltConnectionTokens = FireboltConnectionTokens.builder().build(); @@ -375,17 +375,6 @@ void shouldThrowExceptionWhenPreparingStatementWIthInvalidResultSetType() throws } } - @Test - void shouldGetDefaultEngineWhenEngineIsNotProvided() throws SQLException { - connectionProperties.put("engine", ""); - when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().name("default_engine").endpoint("http://my-endpoint").build()); - try (FireboltConnection connection = createConnection(URL, connectionProperties)) { - verify(fireboltEngineService).getEngine("", "db"); - assertEquals("default_engine", connection.getSessionProperties().getEngine()); - assertEquals("http://my-endpoint", connection.getSessionProperties().getHost()); - } - } - @Test void shouldGetEngineUrlWhenEngineIsProvided() throws SQLException { connectionProperties.put("engine", "engine"); @@ -401,7 +390,7 @@ void shouldNotGetEngineUrlOrDefaultEngineUrlWhenUsingSystemEngine() throws SQLEx connectionProperties.put("database", "my_db"); when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://my_endpoint"); - try (FireboltConnection connection = createConnection(URL, connectionProperties)) { + try (FireboltConnection connection = createConnection(SYSTEM_ENGINE_URL, connectionProperties)) { verify(fireboltEngineService, times(0)).getEngine(isNull(), eq("my_db")); assertEquals("my_endpoint", connection.getSessionProperties().getHost()); } @@ -418,7 +407,6 @@ void noEngineAndDb() throws SQLException { } } - private FireboltConnection createConnection(String url, Properties props) throws FireboltException { return new FireboltConnection(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); } From af46d6613d763beb8040b707d31e736f8e435afd Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Wed, 12 Jul 2023 18:48:56 +0300 Subject: [PATCH 11/15] fixed discovery of engine and validation of database --- .../java/integration/IntegrationTest.java | 1 - .../MockWebServerAwareIntegrationTest.java | 33 ++++++++ .../tests/client/RetryPolicyTest.java | 44 +++------- .../integration/tests/client/TLSTest.java | 36 ++------- .../tests/client/UsageTrackingTest.java | 34 ++------ .../com/firebolt/jdbc/connection/Engine.java | 14 ++-- .../jdbc/connection/FireboltConnection.java | 22 +++-- .../jdbc/service/FireboltEngineService.java | 80 ++++++++----------- .../connection/FireboltConnectionTest.java | 22 +++-- .../service/FireboltEngineServiceTest.java | 50 +++++++----- 10 files changed, 157 insertions(+), 179 deletions(-) create mode 100644 src/integrationTest/java/integration/MockWebServerAwareIntegrationTest.java diff --git a/src/integrationTest/java/integration/IntegrationTest.java b/src/integrationTest/java/integration/IntegrationTest.java index c8628f6b3..6f27b45b6 100644 --- a/src/integrationTest/java/integration/IntegrationTest.java +++ b/src/integrationTest/java/integration/IntegrationTest.java @@ -12,7 +12,6 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; -import java.util.Objects; import static java.util.Objects.requireNonNull; diff --git a/src/integrationTest/java/integration/MockWebServerAwareIntegrationTest.java b/src/integrationTest/java/integration/MockWebServerAwareIntegrationTest.java new file mode 100644 index 000000000..9ddd2cf6c --- /dev/null +++ b/src/integrationTest/java/integration/MockWebServerAwareIntegrationTest.java @@ -0,0 +1,33 @@ +package integration; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import java.io.IOException; + +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public abstract class MockWebServerAwareIntegrationTest extends IntegrationTest { + protected MockWebServer mockBackEnd; + private final int INIT_STATEMENTS_COUNT = 1; // The statement that validates that DB exists. + + @BeforeEach + void setUp() throws IOException { + mockBackEnd = new MockWebServer(); + mockBackEnd.start(); + mockBackEnd.enqueue(new MockResponse().setResponseCode(200).setBody(format("database_name%nstring%n%s", System.getProperty("db")))); + } + + @AfterEach + void tearDown() throws IOException { + mockBackEnd.close(); + } + + + protected void assertMockBackendRequestsCount(int expected) { + assertEquals(INIT_STATEMENTS_COUNT + expected, mockBackEnd.getRequestCount()); + } +} diff --git a/src/integrationTest/java/integration/tests/client/RetryPolicyTest.java b/src/integrationTest/java/integration/tests/client/RetryPolicyTest.java index 44705dd69..5fbfb298d 100644 --- a/src/integrationTest/java/integration/tests/client/RetryPolicyTest.java +++ b/src/integrationTest/java/integration/tests/client/RetryPolicyTest.java @@ -1,47 +1,29 @@ package integration.tests.client; -import static com.firebolt.jdbc.exception.ExceptionType.INVALID_REQUEST; -import static org.junit.jupiter.api.Assertions.*; - -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Statement; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; - -import integration.IntegrationTest; +import integration.MockWebServerAwareIntegrationTest; import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; - -public class RetryPolicyTest extends IntegrationTest { - - private MockWebServer mockBackEnd; +import org.junit.jupiter.api.Test; - @BeforeEach - void setUp() throws IOException { - mockBackEnd = new MockWebServer(); - mockBackEnd.start(); - } +import java.sql.SQLException; +import java.sql.Statement; - @AfterEach - void tearDown() throws IOException { - mockBackEnd.close(); - } +import static com.firebolt.jdbc.exception.ExceptionType.INVALID_REQUEST; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +public class RetryPolicyTest extends MockWebServerAwareIntegrationTest { @Test public void shouldThrowExceptionOn400WithoutRetry() throws SQLException { mockBackEnd.enqueue(new MockResponse().setResponseCode(400)); try (FireboltConnection fireboltConnection = (FireboltConnection) createLocalConnection( - String.format("?ssl=0&port=%d&max_retries=%d", mockBackEnd.getPort(), 3)); + format("?ssl=0&port=%d&max_retries=%d", mockBackEnd.getPort(), 3)); Statement statement = fireboltConnection.createStatement()) { FireboltException ex = assertThrows(FireboltException.class, () -> statement.execute("SELECT 1;")); assertEquals(ex.getType(), INVALID_REQUEST); - assertEquals(1, mockBackEnd.getRequestCount()); + assertMockBackendRequestsCount(1); } } @@ -51,10 +33,10 @@ public void shouldRetryOn502() throws SQLException { mockBackEnd.enqueue(new MockResponse().setResponseCode(502)); mockBackEnd.enqueue(new MockResponse().setResponseCode(200)); try (FireboltConnection fireboltConnection = (FireboltConnection) createLocalConnection( - String.format("?ssl=0&port=%d&max_retries=%d", mockBackEnd.getPort(), 3)); + format("?ssl=0&port=%d&max_retries=%d", mockBackEnd.getPort(), 3)); Statement statement = fireboltConnection.createStatement()) { statement.execute("SELECT 1;"); - assertEquals(3, mockBackEnd.getRequestCount()); + assertMockBackendRequestsCount(3); } } diff --git a/src/integrationTest/java/integration/tests/client/TLSTest.java b/src/integrationTest/java/integration/tests/client/TLSTest.java index 58edfb268..78c9b6ccd 100644 --- a/src/integrationTest/java/integration/tests/client/TLSTest.java +++ b/src/integrationTest/java/integration/tests/client/TLSTest.java @@ -1,6 +1,11 @@ package integration.tests.client; -import static org.junit.jupiter.api.Assertions.assertEquals; +import com.firebolt.jdbc.connection.FireboltConnection; +import integration.MockWebServerAwareIntegrationTest; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.tls.HandshakeCertificates; +import okhttp3.tls.HeldCertificate; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileWriter; @@ -11,32 +16,7 @@ import java.sql.Statement; import java.util.UUID; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.firebolt.jdbc.connection.FireboltConnection; - -import integration.IntegrationTest; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.tls.HandshakeCertificates; -import okhttp3.tls.HeldCertificate; - -public class TLSTest extends IntegrationTest { - private MockWebServer mockBackEnd; - - @BeforeEach - void setUp() throws IOException { - mockBackEnd = new MockWebServer(); - mockBackEnd.start(); - } - - @AfterEach - void tearDown() throws IOException { - mockBackEnd.close(); - } - +public class TLSTest extends MockWebServerAwareIntegrationTest { @Test public void shouldUseTLS() throws SQLException, IOException, NoSuchFieldException, IllegalAccessException { mockBackEnd.enqueue(new MockResponse().setResponseCode(200)); @@ -70,7 +50,7 @@ public void shouldUseTLS() throws SQLException, IOException, NoSuchFieldExceptio String.format("?ssl_certificate_path=%s&port=%s", path, mockBackEnd.getPort())); Statement statement = fireboltConnection.createStatement()) { statement.execute("SELECT 1;"); - assertEquals(1, mockBackEnd.getRequestCount()); + assertMockBackendRequestsCount(1); } finally { removeExistingClient(); } diff --git a/src/integrationTest/java/integration/tests/client/UsageTrackingTest.java b/src/integrationTest/java/integration/tests/client/UsageTrackingTest.java index a11e79ba1..850e3c5d7 100644 --- a/src/integrationTest/java/integration/tests/client/UsageTrackingTest.java +++ b/src/integrationTest/java/integration/tests/client/UsageTrackingTest.java @@ -1,39 +1,19 @@ package integration.tests.client; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Statement; - -import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.util.VersionUtil; - -import integration.IntegrationTest; +import integration.MockWebServerAwareIntegrationTest; import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; -public class UsageTrackingTest extends IntegrationTest { - - private MockWebServer mockBackEnd; - - @BeforeEach - void setUp() throws IOException { - mockBackEnd = new MockWebServer(); - mockBackEnd.start(); - } +import java.sql.SQLException; +import java.sql.Statement; - @AfterEach - void tearDown() throws IOException { - mockBackEnd.close(); - } +import static org.junit.jupiter.api.Assertions.assertTrue; +public class UsageTrackingTest extends MockWebServerAwareIntegrationTest { @Test public void shouldSendRequestWithUserAgentHeaderContainingDriverAndClientInfo() throws SQLException, InterruptedException { diff --git a/src/main/java/com/firebolt/jdbc/connection/Engine.java b/src/main/java/com/firebolt/jdbc/connection/Engine.java index d00040f5c..47f355b60 100644 --- a/src/main/java/com/firebolt/jdbc/connection/Engine.java +++ b/src/main/java/com/firebolt/jdbc/connection/Engine.java @@ -1,13 +1,13 @@ package com.firebolt.jdbc.connection; -import lombok.Builder; -import lombok.Data; +import lombok.*; -@Builder +@AllArgsConstructor @Data +@EqualsAndHashCode public class Engine { - private String endpoint; - private String id; - private String status; - private String name; + private final String endpoint; + private final String status; + private final String name; + private final String database; } diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 722f2c3fe..d731a2379 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -33,6 +33,7 @@ import java.util.*; import java.util.concurrent.Executor; +import static java.lang.String.format; import static java.sql.ResultSet.TYPE_FORWARD_ONLY; @CustomLog @@ -59,7 +60,7 @@ public FireboltConnection(@NonNull String url, Properties connectionSettings, FireboltGatewayUrlService fireboltGatewayUrlService, FireboltStatementService fireboltStatementService, FireboltEngineService fireboltEngineService, - FireboltAccountIdService fireboltAccountIdService) throws FireboltException { + FireboltAccountIdService fireboltAccountIdService) throws SQLException { this.loginProperties = this.extractFireboltProperties(url, connectionSettings); this.fireboltAuthenticationService = fireboltAuthenticationService; @@ -78,7 +79,7 @@ public FireboltConnection(@NonNull String url, Properties connectionSettings, // This code duplication between constructors is done because of back reference: dependent services require reference to current instance of FireboltConnection that prevents using constructor chaining or factory method. @ExcludeFromJacocoGeneratedReport - public FireboltConnection(@NonNull String url, Properties connectionSettings) throws FireboltException { + public FireboltConnection(@NonNull String url, Properties connectionSettings) throws SQLException { this.loginProperties = extractFireboltProperties(url, connectionSettings); OkHttpClient httpClient = getHttpClient(loginProperties); ObjectMapper objectMapper = FireboltObjectMapper.getInstance(); @@ -110,7 +111,7 @@ private <T> FireboltAccountRetriever<T> createFireboltAccountRetriever(OkHttpCli return new FireboltAccountRetriever<>(httpClient, objectMapper, this, loginProperties.getUserDrivers(), loginProperties.getUserClients(), loginProperties.getHost(), path, type); } - private void connect() throws FireboltException { + private void connect() throws SQLException { String accessToken = this.getAccessToken(loginProperties).orElse(StringUtils.EMPTY); closed = false; if (!PropertyUtil.isLocalDb(loginProperties)) { @@ -131,12 +132,20 @@ private void connect() throws FireboltException { //When running packdb locally, the login properties are the session properties sessionProperties = loginProperties; } + assertDatabaseExisting(sessionProperties.getDatabase()); + log.debug("Connection opened"); } - private FireboltProperties getSessionPropertiesForNonSystemEngine() throws FireboltException { + private FireboltProperties getSessionPropertiesForNonSystemEngine() throws SQLException { Engine engine = fireboltEngineService.getEngine(loginProperties.getEngine(), loginProperties.getDatabase()); - return loginProperties.toBuilder().host(engine.getEndpoint()).engine(engine.getName()).systemEngine(false).build(); + return loginProperties.toBuilder().host(engine.getEndpoint()).engine(engine.getName()).systemEngine(false).database(engine.getDatabase()).build(); + } + + private void assertDatabaseExisting(String database) throws SQLException { + if (database != null && !fireboltEngineService.doesDatabaseExist(database)) { + throw new FireboltException(format("Database %s does not exist", database)); + } } private FireboltProperties createInternalSystemEngineProperties(String accessToken, String account) throws FireboltException { @@ -422,8 +431,7 @@ public synchronized void addProperty(Pair<String, String> property) throws Fireb } catch (FireboltException e) { throw e; } catch (Exception e) { - throw new FireboltException( - String.format("Could not set property %s=%s", property.getLeft(), property.getRight()), e); + throw new FireboltException(format("Could not set property %s=%s", property.getLeft(), property.getRight()), e); } } diff --git a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java index ee009cc9c..7b9e35837 100644 --- a/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java +++ b/src/main/java/com/firebolt/jdbc/service/FireboltEngineService.java @@ -1,12 +1,10 @@ package com.firebolt.jdbc.service; -import com.firebolt.jdbc.CheckedFunction; import com.firebolt.jdbc.connection.Engine; import com.firebolt.jdbc.connection.FireboltConnection; import com.firebolt.jdbc.exception.FireboltException; import lombok.CustomLog; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import java.sql.PreparedStatement; @@ -20,19 +18,16 @@ @CustomLog public class FireboltEngineService { private static final String ENGINE_URL = "url"; - private static final String ENGINE_NAME = "engine_name"; - private static final String STATUS_FIELD_NAME = "status"; - private static final String DEFAULT_ENGINE_QUERY = "SELECT engs.url, engs.status, engs.engine_name\n" + - "FROM information_schema.databases AS dbs\n" + - "INNER JOIN information_schema.engines AS engs\n" + - "ON engs.attached_to = dbs.database_name\n" + - "AND engs.engine_name = NULLIF(SPLIT_PART(ARRAY_FIRST(\n" + - " eng_name -> eng_name LIKE '%%(default)',\n" + - " SPLIT(',', attached_engines)\n" + - " ), ' ', 1), '')\n" + - "WHERE database_name = ?"; - private static final String ENGINE_QUERY = "SELECT url, status FROM information_schema.engines WHERE engine_name=?"; + private static final String STATUS_FIELD = "status"; + private static final String ENGINE_NAME_FIELD = "engine_name"; private static final String RUNNING_STATUS = "running"; + private static final String ENGINE_QUERY = + "SELECT engs.url, engs.attached_to, dbs.database_name, engs.status, engs.engine_name " + + "FROM information_schema.engines as engs " + + "LEFT JOIN information_schema.databases as dbs ON engs.attached_to = dbs.database_name " + + "WHERE engs.engine_name = ?"; + private static final String DATABASE_QUERY = "SELECT database_name FROM information_schema.databases WHERE database_name=?"; + private final FireboltConnection fireboltConnection; /** @@ -47,47 +42,42 @@ public String getEngineNameByHost(String engineHost) throws FireboltException { format("Could not establish the engine from the host: %s", engineHost))); } - public Engine getEngine(@Nullable String name, @Nullable String database) throws FireboltException { - if (StringUtils.isEmpty(name)) { - return getDefaultEngine(database); - } else { - return Engine.builder().name(name).endpoint(getEngineEndpoint(name)).build(); + public boolean doesDatabaseExist(String database) throws SQLException { + try (PreparedStatement ps = fireboltConnection.prepareStatement(DATABASE_QUERY)) { + ps.setString(1, database); + try (ResultSet rs = ps.executeQuery()) { + return rs.next(); + } } } - private Engine getDefaultEngine(String database) throws FireboltException { - return getRowValue(DEFAULT_ENGINE_QUERY, database, rs -> Engine.builder().endpoint(rs.getString(ENGINE_URL)).name(rs.getString(ENGINE_NAME)).build(), - "The default engine for the database %s could not be found", - "The default engine for the database %s is not running. Status: %s", - "Could not get default engine url for database %s"); - } - - private String getEngineEndpoint(String engine) throws FireboltException { - return getRowValue(ENGINE_QUERY, engine, rs -> rs.getString(ENGINE_URL), - "The engine with the name %s could not be found", - "The engine with the name %s is not running. Status: %s", - "Could not get engine url for engine %s"); - } - - private <T> T getRowValue(String query, String queryArg, CheckedFunction<ResultSet, T> valueExtractor, String noDataMessage, String engineNotRunningMessage, String sqlFailureMessage) throws FireboltException { - try (PreparedStatement ps = fireboltConnection.prepareStatement(query)) { - ps.setString(1, queryArg); + public Engine getEngine(String engine, @Nullable String database) throws SQLException { + if (engine == null) { + throw new IllegalArgumentException("Cannot retrieve engine parameters because its name is null"); + } + try (PreparedStatement ps = fireboltConnection.prepareStatement(ENGINE_QUERY)) { + ps.setString(1, engine); try (ResultSet rs = ps.executeQuery()) { if (!rs.next()) { - throw new FireboltException(format(noDataMessage, queryArg)); + throw new FireboltException(format("The engine with the name %s could not be found", engine)); + } + String status = rs.getString(STATUS_FIELD); + if (!isEngineRunning(status)) { + throw new FireboltException(format("The engine with the name %s is not running. Status: %s", engine, status)); + } + String attachedDatabase = rs.getString("attached_to"); + if (attachedDatabase == null) { + throw new FireboltException(format("The engine with the name %s is not attached to any database", engine)); } - String status = rs.getString(STATUS_FIELD_NAME); - if (isEngineNotRunning(status)) { - throw new FireboltException(format(engineNotRunningMessage, queryArg, status)); + if (database != null && !database.equals(attachedDatabase)) { + throw new FireboltException(format("The engine with the name %s is not attached to database %s", engine, database)); } - return valueExtractor.apply(rs); + return new Engine(rs.getString(ENGINE_URL), status, rs.getString(ENGINE_NAME_FIELD), attachedDatabase); } - } catch (SQLException e) { - throw new FireboltException(format(sqlFailureMessage, queryArg), e); } } - private boolean isEngineNotRunning(String status) { - return !RUNNING_STATUS.equalsIgnoreCase(status); + private boolean isEngineRunning(String status) { + return RUNNING_STATUS.equalsIgnoreCase(status); } } diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java index f225c9452..898a8f47e 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionTest.java @@ -54,7 +54,7 @@ class FireboltConnectionTest { @BeforeEach - void init() throws FireboltException { + void init() throws SQLException { connectionProperties = new Properties(); connectionProperties.put("client_id", "somebody"); connectionProperties.put("client_secret", "pa$$word"); @@ -64,6 +64,7 @@ void init() throws FireboltException { lenient().when(fireboltGatewayUrlService.getUrl(any(), any())).thenReturn("http://foo:8080/bar"); engine = new Engine("endpoint", "id123", "OK", "noname"); lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(engine); + lenient().when(fireboltEngineService.doesDatabaseExist(any())).thenReturn(true); } @Test @@ -261,7 +262,7 @@ void shouldRemoveExpiredToken() throws SQLException { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); when(fireboltAuthenticationService.getConnectionTokens("http://host:8080", fireboltProperties)) .thenReturn(FireboltConnectionTokens.builder().build()); - lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://hello").build()); + lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(new Engine("http://hello", null, null, null)); try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { fireboltConnection.removeExpiredTokens(); @@ -273,14 +274,12 @@ void shouldRemoveExpiredToken() throws SQLException { @Test void shouldReturnConnectionTokenWhenAvailable() throws SQLException { String accessToken = "hello"; - FireboltProperties fireboltProperties = FireboltProperties.builder().host("host").path("/db").port(8080) - .build(); + FireboltProperties fireboltProperties = FireboltProperties.builder().host("host").path("/db").port(8080).build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); FireboltConnectionTokens connectionTokens = FireboltConnectionTokens.builder().accessToken(accessToken).build(); - when(fireboltAuthenticationService.getConnectionTokens(eq("http://host:8080"), any())) - .thenReturn(connectionTokens); - lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("https://engineHost").build()); + when(fireboltAuthenticationService.getConnectionTokens(eq("http://host:8080"), any())).thenReturn(connectionTokens); + lenient().when(fireboltEngineService.getEngine(any(), any())).thenReturn(new Engine("http://engineHost", null, null, null)); try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { verify(fireboltAuthenticationService).getConnectionTokens("http://host:8080", fireboltProperties); assertEquals(accessToken, fireboltConnection.getAccessToken().get()); @@ -290,8 +289,7 @@ void shouldReturnConnectionTokenWhenAvailable() throws SQLException { @Test void shouldNotReturnConnectionTokenWithLocalDb() throws SQLException { - FireboltProperties fireboltProperties = FireboltProperties.builder().host("localhost").path("/db").port(8080) - .build(); + FireboltProperties fireboltProperties = FireboltProperties.builder().host("localhost").path("/db").port(8080).build(); try (MockedStatic<FireboltProperties> mockedFireboltProperties = Mockito.mockStatic(FireboltProperties.class)) { when(FireboltProperties.of(any())).thenReturn(fireboltProperties); try (FireboltConnection fireboltConnection = createConnection(URL, connectionProperties)) { @@ -354,7 +352,7 @@ void shouldThrowExceptionWhenCannotUnwrap() throws SQLException { void shouldGetDatabaseWhenGettingCatalog() throws SQLException { connectionProperties.put("database", "db"); try (Connection connection = createConnection(URL, connectionProperties)) { - assertEquals("db", connection.getCatalog()); + assertEquals("noname", connection.getCatalog()); // retrieved engine's DB's name is "noname". Firebolt treats DB as catalog } } @@ -378,7 +376,7 @@ void shouldThrowExceptionWhenPreparingStatementWIthInvalidResultSetType() throws @Test void shouldGetEngineUrlWhenEngineIsProvided() throws SQLException { connectionProperties.put("engine", "engine"); - when(fireboltEngineService.getEngine(any(), any())).thenReturn(Engine.builder().endpoint("http://my_endpoint").build()); + when(fireboltEngineService.getEngine(any(), any())).thenReturn(new Engine("http://my_endpoint", null, null, null)); try (FireboltConnection connection = createConnection(URL, connectionProperties)) { verify(fireboltEngineService).getEngine("engine", "db"); assertEquals("http://my_endpoint", connection.getSessionProperties().getHost()); @@ -407,7 +405,7 @@ void noEngineAndDb() throws SQLException { } } - private FireboltConnection createConnection(String url, Properties props) throws FireboltException { + private FireboltConnection createConnection(String url, Properties props) throws SQLException { return new FireboltConnection(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); } } diff --git a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java index 6bca42345..4bf0cc15f 100644 --- a/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java +++ b/src/test/java/com/firebolt/jdbc/service/FireboltEngineServiceTest.java @@ -15,6 +15,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; @@ -22,8 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class FireboltEngineServiceTest { @@ -51,43 +51,51 @@ void shouldThrowExceptionWhenThEngineCannotBeEstablishedFromNullHost() { @Test void shouldGetDefaultEngineWhenEngineNameIsNotProvided() throws SQLException { - PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mockedResultSet(Map.of("status", "running", "url", "https://url", "engine_name", "hello-engine")); - when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); - when(statement.executeQuery()).thenReturn(resultSet); - assertEquals(Engine.builder().endpoint("https://url").name("hello-engine").build(), - fireboltEngineService.getEngine(null, "db")); + assertThrows(IllegalArgumentException.class, () -> fireboltEngineService.getEngine(null, "db")); } @Test void shouldGetEngineWhenEngineNameIsProvided() throws SQLException { PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mockedResultSet(Map.of("status", "running", "url", "https://url")); + ResultSet resultSet = mockedResultSet(Map.of("status", "running", "url", "https://url", "attached_to", "db", "engine_name", "some-engine")); when(fireboltConnection.prepareStatement(anyString())).thenReturn(statement); when(statement.executeQuery()).thenReturn(resultSet); - assertEquals(Engine.builder().endpoint("https://url").name("some-engine").build(), - fireboltEngineService.getEngine("some-engine", "db")); + assertEquals(new Engine("https://url", "running", "some-engine", "db"), fireboltEngineService.getEngine("some-engine", "db")); } @ParameterizedTest @CsvSource(value = { - "down;some-engine;db;SELECT url, status", - "down;;db;SELECT.+JOIN", - "failed;'';db;SELECT.+JOIN", + "engine1;db1;http://url1;running;;The engine with the name engine1 is not attached to any database", + "engine1;db1;http://url1;running;db2;The engine with the name engine1 is not attached to database db1", + "engine1;db1;http://url1;starting;;The engine with the name engine1 is not running. Status: starting", + "engine2;;;;;The engine with the name engine2 could not be found", }, delimiter = ';') - void shouldThrowExceptionWhenEngineNotRunning(String status, String engineName, String db, String queryRegex) throws SQLException { + void shouldThrowExceptionWhenSomethingIsWrong(String engineName, String db, String endpoint, String status, String attachedDb, String errorMessage) throws SQLException { PreparedStatement statement = mock(PreparedStatement.class); - ResultSet resultSet = mockedResultSet(Map.of("status", status)); - when(fireboltConnection.prepareStatement(Mockito.matches(Pattern.compile(queryRegex, Pattern.MULTILINE | Pattern.DOTALL)))).thenReturn(statement); + Map<String, String> rsData = null; + if (endpoint != null || status != null || attachedDb != null) { + rsData = new HashMap<>(); + rsData.put("url", endpoint); + rsData.put("status", status); + rsData.put("attached_to", attachedDb); + rsData.put("engine_name", engineName); + } + ResultSet resultSet = mockedResultSet(rsData); + when(fireboltConnection.prepareStatement(Mockito.matches(Pattern.compile("SELECT.+JOIN", Pattern.MULTILINE | Pattern.DOTALL)))).thenReturn(statement); when(statement.executeQuery()).thenReturn(resultSet); - assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(engineName, db)); + assertEquals(errorMessage, assertThrows(FireboltException.class, () -> fireboltEngineService.getEngine(engineName, db)).getMessage()); + Mockito.verify(statement, Mockito.times(1)).setString(1, engineName); } private ResultSet mockedResultSet(Map<String, String> values) throws SQLException { ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(true); - for (Entry<String, String> column : values.entrySet()) { - when(resultSet.getString(column.getKey())).thenReturn(column.getValue()); + if (values == null) { + when(resultSet.next()).thenReturn(false); + } else { + when(resultSet.next()).thenReturn(true, false); + for (Entry<String, String> column : values.entrySet()) { + lenient().when(resultSet.getString(column.getKey())).thenReturn(column.getValue()); + } } return resultSet; } From e4237e83bfb17e2b2746385374c5f22a07fc1cf3 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Thu, 20 Jul 2023 12:32:09 +0300 Subject: [PATCH 12/15] FIR-25124 Removed /dynamic/query path for system engine in JDBC --- .../com/firebolt/jdbc/client/query/StatementClientImpl.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java index af06cfc93..11e2e7c94 100644 --- a/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java +++ b/src/main/java/com/firebolt/jdbc/client/query/StatementClientImpl.java @@ -36,9 +36,6 @@ public class StatementClientImpl extends FireboltClient implements StatementClie private static final String TAB_SEPARATED_WITH_NAMES_AND_TYPES_FORMAT = "TabSeparatedWithNamesAndTypes"; - private static final List<String> URI_QUERY_SEGMENTS = Arrays.asList("dynamic","query"); - - private final BiPredicate<Call, String> isCallWithId = (call, id) -> call.request().tag() instanceof String && StringUtils.equals((String) call.request().tag(), id); @@ -175,7 +172,7 @@ public boolean isStatementRunning(String statementId) { } private URI buildQueryUri(FireboltProperties fireboltProperties, Map<String, String> parameters) { - return buildURI(fireboltProperties, parameters, PropertyUtil.isLocalDb(fireboltProperties) || !fireboltProperties.isSystemEngine() ? Collections.emptyList() : URI_QUERY_SEGMENTS); + return buildURI(fireboltProperties, parameters, Collections.emptyList()); } private URI buildCancelUri(FireboltProperties fireboltProperties, String id) { From 798962424a5403e3bbdaa04dd6d05b8888c48850 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Thu, 20 Jul 2023 12:33:02 +0300 Subject: [PATCH 13/15] changed TimeoutTest that stopped working in current version --- .../java/integration/tests/TimeoutTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/integrationTest/java/integration/tests/TimeoutTest.java b/src/integrationTest/java/integration/tests/TimeoutTest.java index 6fcca31c7..81b72be2a 100644 --- a/src/integrationTest/java/integration/tests/TimeoutTest.java +++ b/src/integrationTest/java/integration/tests/TimeoutTest.java @@ -3,8 +3,11 @@ import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; +import java.sql.SQLException; import java.sql.Statement; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -17,12 +20,12 @@ class TimeoutTest extends IntegrationTest { @Test @Timeout(value = 7, unit = TimeUnit.MINUTES) - void shouldExecuteRequestWithoutTimeout() { + void shouldExecuteRequestWithoutTimeout() throws SQLException { long startTime = System.nanoTime(); try (Connection con = this.createConnection(); Statement stmt = con.createStatement()) { - this.setParam(con, "use_standard_sql", "0"); - this.setParam(con, "advanced_mode", "1"); - stmt.executeQuery("SELECT sleepEachRow(1) from numbers(360)"); + String numbers = IntStream.range(1, 10_000).boxed().map(String::valueOf).collect(Collectors.joining(",")); + String query = String.format("WITH arr AS (SELECT [%s] as a)%nSELECT md5(md5(md5(md5(md5(md5(md5(to_string(a)))))))) FROM arr UNNEST(a)", numbers); + stmt.executeQuery(query); } catch (Exception e) { log.error("Error", e); fail(); From 3ff508c97c2be9a1bfc72f63d2d7864b96256453 Mon Sep 17 00:00:00 2001 From: alexradzin <alexander.radzin@firebolt.io> Date: Thu, 20 Jul 2023 12:40:16 +0300 Subject: [PATCH 14/15] FIR-25124 Removed /dynamic/query path for system engine in JDBC - fixed unit test --- .../com/firebolt/jdbc/client/query/StatementClientImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index f9f12943d..044a092dd 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -55,7 +55,7 @@ void shouldPostSqlQueryWithExpectedUrl() throws FireboltException, IOException { void shouldPostSqlQueryForSystemEngine() throws FireboltException, IOException { String url = shouldPostSqlQueryForSystemEngine(true).getValue(); assertEquals( - "http://firebolt1:555/dynamic/query?result_overflow_mode=break&database=db1&account_id=12345&output_format=TabSeparatedWithNamesAndTypes&max_result_rows=1&max_execution_time=15", + "http://firebolt1:555/?result_overflow_mode=break&database=db1&account_id=12345&output_format=TabSeparatedWithNamesAndTypes&max_result_rows=1&max_execution_time=15", url); } From 218ec2e3fad655233e5cc7c7b0daf32e5747e330 Mon Sep 17 00:00:00 2001 From: Stepan Burlakov <stepansergeevitch@gmail.com> Date: Thu, 20 Jul 2023 15:33:43 +0300 Subject: [PATCH 15/15] ci: new identity (#245) --- .github/workflows/integration-test.yml | 47 ++++++++++++++++---------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index d6148db1e..8b8ad9d6d 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -8,12 +8,12 @@ on: required: false default: '' engine: - description: 'Engine' - required: true + description: 'Engine - a new one will be created if not provided' + required: false account: description: 'Account' required: true - default: 'integration' + default: 'developer' environment: description: 'Environment to run the tests against' type: choice @@ -28,6 +28,13 @@ jobs: runs-on: ubuntu-latest steps: + - name: Validate database and engine + if: ${{ (github.event.inputs.database == '') != (github.event.inputs.engine == '') }} + uses: actions/github-script@v3 + with: + script: | + core.setFailed("Database and Engine parameters should be provided simultaneously") + - name: "Foresight: Collect Workflow Telemetry" uses: runforesight/foresight-workflow-kit-action@v1 if: ${{ always() }} @@ -42,25 +49,22 @@ jobs: - name: Determine env variables run: | if [ "${{ github.event.inputs.environment }}" == 'staging' ]; then - echo "USERNAME=${{ secrets.FIREBOLT_USERNAME_STAGING }}" >> "$GITHUB_ENV" - echo "PASSWORD=${{ secrets.FIREBOLT_PASSWORD_STAGING }}" >> "$GITHUB_ENV" - echo "SERVICE_ACCOUNT_ID=${{ secrets.SERVICE_ACCOUNT_ID_STAGING }}" >> "$GITHUB_ENV" - echo "SERVICE_ACCOUNT_SECRET=${{ secrets.SERVICE_ACCOUNT_SECRET_STAGING }}" >> "$GITHUB_ENV" + echo "SERVICE_ACCOUNT_ID=${{ secrets.FIREBOLT_CLIENT_ID_STG_NEW_IDN }}" >> "$GITHUB_ENV" + echo "SERVICE_ACCOUNT_SECRET=${{ secrets.FIREBOLT_CLIENT_SECRET_STG_NEW_IDN }}" >> "$GITHUB_ENV" else - echo "USERNAME=${{ secrets.FIREBOLT_USERNAME_DEV }}" >> "$GITHUB_ENV" - echo "PASSWORD=${{ secrets.FIREBOLT_PASSWORD_DEV }}" >> "$GITHUB_ENV" - echo "SERVICE_ACCOUNT_ID=${{ secrets.SERVICE_ACCOUNT_ID_DEV }}" >> "$GITHUB_ENV" - echo "SERVICE_ACCOUNT_SECRET=${{ secrets.SERVICE_ACCOUNT_SECRET_DEV }}" >> "$GITHUB_ENV" + echo "SERVICE_ACCOUNT_ID=${{ secrets.FIREBOLT_CLIENT_ID_NEW_IDN }}" >> "$GITHUB_ENV" + echo "SERVICE_ACCOUNT_SECRET=${{ secrets.FIREBOLT_CLIENT_SECRET_NEW_IDN }}" >> "$GITHUB_ENV" fi + - name: Setup database and engine id: setup if: ${{ github.event.inputs.database == '' }} - uses: firebolt-db/integration-testing-setup@v1 + uses: firebolt-db/integration-testing-setup@v2 with: - firebolt-username: ${{ env.USERNAME }} - firebolt-password: ${{ env.PASSWORD }} + firebolt-client-id: ${{ env.SERVICE_ACCOUNT_ID }} + firebolt-client-secret: ${{ env.SERVICE_ACCOUNT_SECRET }} + account: ${{ github.event.inputs.account }} api-endpoint: "api.${{ github.event.inputs.environment }}.firebolt.io" - region: "us-east-1" instance-type: "B2" - name: Determine database name @@ -72,8 +76,17 @@ jobs: echo "database_name=${{ steps.setup.outputs.database_name }}" >> $GITHUB_OUTPUT fi + - name: Determine engine name + id: find-engine-name + run: | + if ! [[ -z "${{ github.event.inputs.engine }}" ]]; then + echo "engine_name=${{ github.event.inputs.engine }}" >> $GITHUB_OUTPUT + else + echo "engine_name=${{ steps.setup.outputs.engine_name }}" >> $GITHUB_OUTPUT + fi + - name: Run integration tests - run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }}" -Daccount="${{ github.event.inputs.account }}" -Dengine="${{ github.event.inputs.engine }}" + run: ./gradlew integrationTest -Ddb=${{ steps.find-database-name.outputs.database_name }} -Denv=${{ github.event.inputs.environment }} -Dclient_secret="${{ env.SERVICE_ACCOUNT_SECRET }}" -Dclient_id="${{ env.SERVICE_ACCOUNT_ID }}" -Daccount="${{ github.event.inputs.account }}" -Dengine="${{ steps.find-engine-name.outputs.engine_name }}" - name: "Foresight: Analyze Test Results" uses: runforesight/foresight-test-kit-action@v1 @@ -84,4 +97,4 @@ jobs: test_path: ./build/test-results/ tags: | type:"integration" - language:"Java" \ No newline at end of file + language:"Java"