Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Obtaining url from database metadata #52

Merged
merged 2 commits into from
Mar 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = true
ij_java_keep_simple_methods_in_one_line = true
ij_java_names_count_to_use_import_on_demand = 101
ij_java_class_count_to_use_import_on_demand = 101
ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
ij_java_prefer_longer_names = true
ij_java_space_after_closing_angle_bracket_in_type_argument = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import io.github.mfvanek.pg.checks.host.UnusedIndexesCheckOnHost;
import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.connection.PgConnectionImpl;
import io.github.mfvanek.pg.connection.PgHost;
import io.github.mfvanek.pg.connection.PgHostImpl;
import io.github.mfvanek.pg.connection.PgSqlException;
import io.github.mfvanek.pg.settings.maintenance.ConfigurationMaintenanceOnHost;
import io.github.mfvanek.pg.settings.maintenance.ConfigurationMaintenanceOnHostImpl;
import io.github.mfvanek.pg.statistics.maintenance.StatisticsMaintenanceOnHost;
Expand All @@ -43,6 +45,9 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
import javax.sql.DataSource;

/**
Expand All @@ -64,9 +69,18 @@ public class DatabaseStructureHealthAutoConfiguration {
@ConditionalOnBean(name = "dataSource")
@ConditionalOnMissingBean
public PgConnection pgConnection(@Qualifier("dataSource") final DataSource dataSource,
@Value("${spring.datasource.url:jdbc:postgresql://localhost:5432}") final String databaseUrl) {
// TODO Try to obtain URL from dataSource.getConnection().getMetaData().getURL()
return PgConnectionImpl.of(dataSource, PgHostImpl.ofUrl(databaseUrl));
@Value("${spring.datasource.url:#{null}}") final String databaseUrl) {
final PgHost host;
if (Objects.isNull(databaseUrl) || databaseUrl.isBlank()) {
try (Connection connection = dataSource.getConnection()) {
host = PgHostImpl.ofUrl(connection.getMetaData().getURL());
} catch (SQLException ex) {
throw new PgSqlException(ex);
}
} else {
host = PgHostImpl.ofUrl(databaseUrl);
}
return PgConnectionImpl.of(dataSource, host);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ abstract class AutoConfigurationTestBase {
"configurationMaintenanceOnHost"
);
protected static final Class<?>[] EXPECTED_TYPES = {PgConnection.class, DatabaseCheckOnHost.class, StatisticsMaintenanceOnHost.class, ConfigurationMaintenanceOnHost.class};
protected static final DataSource DATA_SOURCE_MOCK = Mockito.mock(DataSource.class);

protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
protected final Predicate<String> beanNamesFilter = b -> !b.startsWith("org.springframework") && !b.startsWith("pg.index.health.test") &&
!b.endsWith("AutoConfiguration") && !"dataSource".equals(b);
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();

@Nonnull
protected ApplicationContextRunner assertWithTestConfig() {
Expand All @@ -61,7 +62,7 @@ protected ApplicationContextRunner assertWithTestConfig() {

protected static <C extends ConfigurableApplicationContext> void initialize(@Nonnull final C applicationContext) {
final GenericApplicationContext context = (GenericApplicationContext) applicationContext;
context.registerBean("dataSource", DataSource.class, () -> Mockito.mock(DataSource.class));
context.registerBean("dataSource", DataSource.class, () -> DATA_SOURCE_MOCK);
}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ void withoutPgConnectionClass() {
ConfigurationMaintenanceOnHost.class})
void withoutClass(final Class<?> type) {
assertWithTestConfig()
.withPropertyValues("spring.datasource.url=jdbc:postgresql://localhost:5432")
.withInitializer(AutoConfigurationTestBase::initialize)
.withClassLoader(new FilteredClassLoader(type))
.run(context -> assertThat(context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,20 @@

package io.github.mfvanek.pg.spring;

import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.connection.PgHostImpl;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ConfigurableApplicationContext;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.annotation.Nonnull;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class DatabaseStructureHealthAutoConfigurationTest extends AutoConfigurationTestBase {

Expand All @@ -27,16 +38,45 @@ void withoutDataSource() {
@Test
void withDataSource() {
assertWithTestConfig()
.withPropertyValues("spring.datasource.url=jdbc:postgresql://localhost:5432")
.withInitializer(AutoConfigurationTestBase::initialize)
.run(context -> {
assertThat(context.getBeanDefinitionNames())
.filteredOn(beanNamesFilter)
.hasSameSizeAs(EXPECTED_BEANS)
.containsAll(EXPECTED_BEANS);
assertThatBeansPresent(context);
assertThatBeansAreNotNullBean(context);
});
}

@Test
void withDataSourceButWithoutConnectionString() throws SQLException {
try (Connection connectionMock = Mockito.mock(Connection.class)) {
setMocks(connectionMock);

assertWithTestConfig()
.withInitializer(AutoConfigurationTestBase::initialize)
.run(context -> {
assertThatBeansPresent(context);
assertThatBeansAreNotNullBean(context);
assertThatPgConnectionIsValid(context);
});
}
}

@Test
void withDataSourceAndEmptyConnectionString() throws SQLException {
try (Connection connectionMock = Mockito.mock(Connection.class)) {
setMocks(connectionMock);

assertWithTestConfig()
.withPropertyValues("spring.datasource.url=")
.withInitializer(AutoConfigurationTestBase::initialize)
.run(context -> {
assertThatBeansPresent(context);
assertThatBeansAreNotNullBean(context);
assertThatPgConnectionIsValid(context);
});
}
}

@Test
void shouldNotCreateAutoConfigurationWithDisabledProperty() {
assertWithTestConfig()
Expand All @@ -51,15 +91,55 @@ void shouldNotCreateAutoConfigurationWithDisabledProperty() {
@Test
void shouldCreateAutoConfigurationWhenPropertyExplicitlySet() {
assertWithTestConfig()
.withPropertyValues("pg.index.health.test.enabled=true")
.withPropertyValues("pg.index.health.test.enabled=true",
"spring.datasource.url=jdbc:postgresql://localhost:5432")
.withInitializer(AutoConfigurationTestBase::initialize)
.run(context -> {
assertThat(context.getBeanDefinitionNames())
.isNotEmpty()
.filteredOn(beanNamesFilter)
.hasSameSizeAs(EXPECTED_BEANS)
.containsAll(EXPECTED_BEANS);
assertThatBeansPresent(context);
assertThatBeansAreNotNullBean(context);
});
}

@Test
void withDataSourceAndExceptionWhileObtainingUrlFromMetadata() throws SQLException {
try (Connection connectionMock = Mockito.mock(Connection.class)) {
Mockito.when(DATA_SOURCE_MOCK.getConnection())
.thenReturn(connectionMock);
Mockito.when(connectionMock.getMetaData())
.thenThrow(SQLException.class);

final ApplicationContextRunner contextRunner = assertWithTestConfig()
.withInitializer(AutoConfigurationTestBase::initialize);
assertThatThrownBy(() -> contextRunner.run(this::assertThatPgConnectionIsValid))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unstarted application context org.springframework.boot.test.context.assertj.AssertableApplicationContext[" +
"startupFailure=org.springframework.beans.factory.BeanCreationException] failed to start")
.hasStackTraceContaining("Factory method 'pgConnection' threw exception; nested exception is io.github.mfvanek.pg.connection.PgSqlException");
}
}

private void assertThatBeansPresent(@Nonnull final ConfigurableApplicationContext context) {
assertThat(context.getBeanDefinitionNames())
.isNotEmpty()
.filteredOn(beanNamesFilter)
.hasSameSizeAs(EXPECTED_BEANS)
.containsAll(EXPECTED_BEANS);
}

private void assertThatPgConnectionIsValid(@Nonnull final ConfigurableApplicationContext context) {
assertThat(context.getBean("pgConnection", PgConnection.class))
.isNotNull()
.satisfies(c -> assertThat(c.getHost())
.isEqualTo(PgHostImpl.ofUrl("jdbc:postgresql://192.168.1.1:6432")));
}

private void setMocks(@Nonnull final Connection connectionMock) throws SQLException {
Mockito.when(DATA_SOURCE_MOCK.getConnection())
.thenReturn(connectionMock);
final DatabaseMetaData metaDataMock = Mockito.mock(DatabaseMetaData.class);
Mockito.when(connectionMock.getMetaData())
.thenReturn(metaDataMock);
Mockito.when(metaDataMock.getURL())
.thenReturn("jdbc:postgresql://192.168.1.1:6432");
}
}