Skip to content

Commit

Permalink
feat: add clickhouse r2dbc support
Browse files Browse the repository at this point in the history
  • Loading branch information
livk-cloud committed Mar 14, 2024
1 parent d3b0df2 commit 083dcde
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 10 deletions.
9 changes: 8 additions & 1 deletion modules/clickhouse/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ dependencies {
api project(':testcontainers')
api project(':jdbc')

compileOnly project(':r2dbc')
compileOnly(group: 'com.clickhouse', name: 'clickhouse-r2dbc', version: '0.6.0', classifier: 'http')

testImplementation project(':jdbc-test')
testRuntimeOnly 'ru.yandex.clickhouse:clickhouse-jdbc:0.3.2'
testRuntimeOnly(group: 'com.clickhouse', name: 'clickhouse-jdbc', version: '0.6.0', classifier: 'http')
testImplementation 'org.assertj:assertj-core:3.25.1'
testImplementation testFixtures(project(':r2dbc'))
testRuntimeOnly(group: 'com.clickhouse', name: 'clickhouse-r2dbc', version: '0.6.0', classifier: 'http')

testRuntimeOnly("org.apache.httpcomponents.client5:httpclient5:5.2.1")
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public class ClickHouseContainer extends JdbcDatabaseContainer<ClickHouseContain

private static final DockerImageName CLICKHOUSE_IMAGE_NAME = DockerImageName.parse("clickhouse/clickhouse-server");

private static final Integer HTTP_PORT = 8123;
public static final Integer HTTP_PORT = 8123;

private static final Integer NATIVE_PORT = 9000;
static final Integer NATIVE_PORT = 9000;

private static final String DRIVER_CLASS_NAME = "com.clickhouse.jdbc.ClickHouseDriver";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.testcontainers.clickhouse;

import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.containers.ClickHouseR2DBCDatabaseContainerProvider;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;

/**
* ClickHouse R2DBC support
*/
public class ClickHouseR2DBCDatabaseContainer implements R2DBCDatabaseContainer {

private final ClickHouseContainer container;

public ClickHouseR2DBCDatabaseContainer(ClickHouseContainer container) {
this.container = container;
}

public static ConnectionFactoryOptions getOptions(ClickHouseContainer container) {
ConnectionFactoryOptions options = ConnectionFactoryOptions.builder()
.option(ConnectionFactoryOptions.DRIVER, ClickHouseR2DBCDatabaseContainerProvider.DRIVER)
.build();

return new ClickHouseR2DBCDatabaseContainer(container).configure(options);
}

@Override
public void start() {
container.start();
}

@Override
public void stop() {
container.stop();
}

@Override
public ConnectionFactoryOptions configure(ConnectionFactoryOptions options) {
return options.mutate()
.option(ConnectionFactoryOptions.HOST, container.getHost())
.option(ConnectionFactoryOptions.PORT, container.getMappedPort(ClickHouseContainer.HTTP_PORT))
.option(ConnectionFactoryOptions.DATABASE, container.getDatabaseName())
.option(ConnectionFactoryOptions.USER, container.getUsername())
.option(ConnectionFactoryOptions.PASSWORD, container.getPassword())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.testcontainers.containers;

import com.clickhouse.r2dbc.connection.ClickHouseConnectionFactoryProvider;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.clickhouse.ClickHouseContainer;
import org.testcontainers.clickhouse.ClickHouseR2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainer;
import org.testcontainers.r2dbc.R2DBCDatabaseContainerProvider;

public class ClickHouseR2DBCDatabaseContainerProvider implements R2DBCDatabaseContainerProvider {
public static final String DRIVER = ClickHouseConnectionFactoryProvider.CLICKHOUSE_DRIVER;

private static final String IMAGE_NAME = "clickhouse/clickhouse-server";

@Override
public boolean supports(ConnectionFactoryOptions options) {
return DRIVER.equals(options.getRequiredValue(ConnectionFactoryOptions.DRIVER));
}

@Override
public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options) {
String image = IMAGE_NAME + ":" + options.getRequiredValue(IMAGE_TAG_OPTION);
ClickHouseContainer container = new ClickHouseContainer(image)
.withDatabaseName((String) options.getRequiredValue(ConnectionFactoryOptions.DATABASE));

if (Boolean.TRUE.equals(options.getValue(REUSABLE_OPTION))) {
container.withReuse(true);
}
return new ClickHouseR2DBCDatabaseContainer(container);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.testcontainers.containers.ClickHouseR2DBCDatabaseContainerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.testcontainers.clickhouse;

import io.r2dbc.spi.ConnectionFactoryOptions;
import org.testcontainers.r2dbc.AbstractR2DBCDatabaseContainerTest;

public class ClickHouseR2DBCDatabaseContainerTest extends AbstractR2DBCDatabaseContainerTest<ClickHouseContainer> {
@Override
protected ConnectionFactoryOptions getOptions(ClickHouseContainer container) {
// spotless:off
// get_options {
ConnectionFactoryOptions options = ClickHouseR2DBCDatabaseContainer.getOptions(
container
);
// }
// spotless:on

return options;
}

@Override
protected String createR2DBCUrl() {
return "r2dbc:tc:clickhouse:///db?TC_IMAGE_TAG=21.9.2-alpine";
}

@Override
protected ClickHouseContainer createContainer() {
return new ClickHouseContainer("clickhouse/clickhouse-server:21.9.2-alpine")
.withExposedPorts(ClickHouseContainer.HTTP_PORT, ClickHouseContainer.NATIVE_PORT)
.withUsername("test")
.withPassword("test")
.withDatabaseName("db");
}
}
16 changes: 9 additions & 7 deletions modules/r2dbc/src/main/java/org/testcontainers/r2dbc/Hidden.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@ private ConnectionFactoryOptions removeProxying(ConnectionFactoryOptions options
String[] protocols = protocol.split(":", 2);
String driverDelegate = protocols[0];

// when protocol does NOT contain COLON, the length becomes 1
String protocolDelegate = protocols.length == 2 ? protocols[1] : "";

return ConnectionFactoryOptions
ConnectionFactoryOptions.Builder builder = ConnectionFactoryOptions
.builder()
.from(options)
.option(ConnectionFactoryOptions.DRIVER, driverDelegate)
.option(ConnectionFactoryOptions.PROTOCOL, protocolDelegate)
.build();
.option(ConnectionFactoryOptions.DRIVER, driverDelegate);

if (protocols.length == 2) {
// when protocol does NOT contain COLON, the length becomes 1
builder.option(ConnectionFactoryOptions.PROTOCOL, protocols[1]);
}

return builder.build();
}

@Override
Expand Down

0 comments on commit 083dcde

Please sign in to comment.