Skip to content

Commit fe56f70

Browse files
authored
Merge pull request #46326 from Malandril/mongo-tls-liquibase
Use the quarkus MongoClients in the liquibase-mongodb extension
2 parents 57e6041 + 240d769 commit fe56f70

File tree

8 files changed

+84
-60
lines changed

8 files changed

+84
-60
lines changed

Diff for: docs/src/main/asciidoc/liquibase-mongodb.adoc

+3-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ The following is an example for the `{config-file}` file:
6666
[source,properties]
6767
----
6868
# configure MongoDB
69-
quarkus.mongodb.connection-string = mongodb://localhost:27017
69+
quarkus.mongodb.connection-string = mongodb://localhost:27017/mydatabase
7070
7171
# Liquibase MongoDB minimal config properties
7272
quarkus.liquibase-mongodb.migrate-at-start=true
@@ -80,8 +80,9 @@ quarkus.liquibase-mongodb.migrate-at-start=true
8080
# quarkus.liquibase-mongodb.default-catalog-name=DefaultCatalog
8181
# quarkus.liquibase-mongodb.default-schema-name=DefaultSchema
8282
----
83+
NOTE: Liquibase needs a database either in the connection string or with the `quarkus.mongodb.database` property.
8384

84-
NOTE: Liquibase MongoDB is configured using a connection string, we do our best to craft a connection string that matches the MongoDB client configuration but if some configuration properties are not working you may consider adding them directly into the `quarkus.mongodb.connection-string` config property.
85+
NOTE: By default, Liquibase MongoDB is configured to use the default MongoDB client Quarkus creates, but you can configure the extension to use a named client by setting `quarkus.liquibase-mongodb.mongo-client-name`.
8586

8687
Add a changeLog file to the default folder following the Liquibase naming conventions: `{change-log}`
8788
YAML, JSON and XML formats are supported for the changeLog.

Diff for: extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java

+59-51
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,45 @@
33
import java.io.FileNotFoundException;
44
import java.nio.file.Paths;
55
import java.util.Map;
6+
import java.util.Optional;
67
import java.util.regex.Matcher;
78
import java.util.regex.Pattern;
8-
import java.util.stream.Collectors;
99

10+
import com.mongodb.client.MongoClient;
11+
12+
import io.quarkus.arc.Arc;
1013
import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbBuildTimeConfig;
1114
import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbConfig;
15+
import io.quarkus.mongodb.runtime.MongoClientBeanUtil;
1216
import io.quarkus.mongodb.runtime.MongoClientConfig;
17+
import io.quarkus.mongodb.runtime.MongoClients;
18+
import io.quarkus.mongodb.runtime.MongodbConfig;
1319
import io.quarkus.runtime.util.StringUtil;
1420
import liquibase.Contexts;
1521
import liquibase.LabelExpression;
1622
import liquibase.Liquibase;
1723
import liquibase.database.Database;
18-
import liquibase.database.DatabaseFactory;
24+
import liquibase.ext.mongodb.database.MongoConnection;
25+
import liquibase.ext.mongodb.database.MongoLiquibaseDatabase;
1926
import liquibase.resource.ClassLoaderResourceAccessor;
2027
import liquibase.resource.CompositeResourceAccessor;
2128
import liquibase.resource.DirectoryResourceAccessor;
2229
import liquibase.resource.ResourceAccessor;
2330

2431
public class LiquibaseMongodbFactory {
2532

26-
private final MongoClientConfig mongoClientConfig;
27-
private final LiquibaseMongodbConfig liquibaseMongodbConfig;
28-
private final LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig;
29-
3033
//connection-string format, see https://docs.mongodb.com/manual/reference/connection-string/
31-
Pattern HAS_DB = Pattern
34+
private static final Pattern HAS_DB = Pattern
3235
.compile("(?<prefix>mongodb://|mongodb\\+srv://)(?<hosts>[^/]*)(?<slash>[/]?)(?<db>[^?]*)(?<options>\\??.*)");
36+
private final LiquibaseMongodbConfig liquibaseMongodbConfig;
37+
private final LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig;
38+
private final MongodbConfig mongodbConfig;
3339

3440
public LiquibaseMongodbFactory(LiquibaseMongodbConfig config,
35-
LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, MongoClientConfig mongoClientConfig) {
41+
LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, MongodbConfig mongodbConfig) {
3642
this.liquibaseMongodbConfig = config;
3743
this.liquibaseMongodbBuildTimeConfig = liquibaseMongodbBuildTimeConfig;
38-
this.mongoClientConfig = mongoClientConfig;
44+
this.mongodbConfig = mongodbConfig;
3945
}
4046

4147
private ResourceAccessor resolveResourceAccessor() throws FileNotFoundException {
@@ -83,54 +89,46 @@ private String parseChangeLog(String changeLog) {
8389

8490
public Liquibase createLiquibase() {
8591
try (ResourceAccessor resourceAccessor = resolveResourceAccessor()) {
92+
MongoClients mongoClients = Arc.container().instance(MongoClients.class).get();
93+
String mongoClientName;
94+
MongoClientConfig mongoClientConfig;
95+
if (liquibaseMongodbConfig.mongoClientName().isPresent()) {
96+
mongoClientName = liquibaseMongodbConfig.mongoClientName().get();
97+
mongoClientConfig = mongodbConfig.mongoClientConfigs().get(mongoClientName);
98+
if (mongoClientConfig == null) {
99+
throw new IllegalArgumentException("Mongo client named '%s' not found".formatted(mongoClientName));
100+
}
101+
} else {
102+
mongoClientConfig = mongodbConfig.defaultMongoClientConfig();
103+
mongoClientName = MongoClientBeanUtil.DEFAULT_MONGOCLIENT_NAME;
104+
}
86105
String parsedChangeLog = parseChangeLog(liquibaseMongodbBuildTimeConfig.changeLog());
87-
String connectionString = this.mongoClientConfig.connectionString().orElse("mongodb://localhost:27017");
88-
89-
// Every MongoDB client configuration must be added to the connection string, we didn't add all as it would be too much to support.
90-
// For reference, all connections string options can be found here: https://www.mongodb.com/docs/manual/reference/connection-string/#connection-string-options.
91-
106+
String connectionString = mongoClientConfig.connectionString().orElse("mongodb://localhost:27017");
92107
Matcher matcher = HAS_DB.matcher(connectionString);
93-
if (!matcher.matches() || matcher.group("db") == null || matcher.group("db").isEmpty()) {
94-
connectionString = matcher.replaceFirst(
95-
"${prefix}${hosts}/"
96-
+ this.mongoClientConfig.database()
97-
.orElseThrow(() -> new IllegalArgumentException("Config property " +
98-
"'quarkus.mongodb.database' must be defined when no database exist in the connection string"))
99-
+ "${options}");
108+
Optional<String> maybeDatabase = mongoClientConfig.database();
109+
if (maybeDatabase.isEmpty()) {
110+
if (matcher.matches() && !StringUtil.isNullOrEmpty(matcher.group("db"))) {
111+
maybeDatabase = Optional.of(matcher.group("db"));
112+
} else {
113+
throw new IllegalArgumentException("Config property 'quarkus.mongodb.database' must " +
114+
"be defined when no database exist in the connection string");
115+
}
100116
}
101-
if (mongoClientConfig.credentials().authSource().isPresent()) {
102-
boolean alreadyHasQueryParams = connectionString.contains("?");
103-
connectionString += (alreadyHasQueryParams ? "&" : "?") + "authSource="
104-
+ mongoClientConfig.credentials().authSource().get();
117+
Database database = createDatabase(mongoClients, mongoClientName, maybeDatabase.get());
118+
if (liquibaseMongodbConfig.liquibaseCatalogName().isPresent()) {
119+
database.setLiquibaseCatalogName(liquibaseMongodbConfig.liquibaseCatalogName().get());
105120
}
106-
if (mongoClientConfig.credentials().authMechanism().isPresent()) {
107-
boolean alreadyHasQueryParams = connectionString.contains("?");
108-
connectionString += (alreadyHasQueryParams ? "&" : "?") + "authMechanism="
109-
+ mongoClientConfig.credentials().authMechanism().get();
121+
if (liquibaseMongodbConfig.liquibaseSchemaName().isPresent()) {
122+
database.setLiquibaseSchemaName(liquibaseMongodbConfig.liquibaseSchemaName().get());
110123
}
111-
if (!mongoClientConfig.credentials().authMechanismProperties().isEmpty()) {
112-
boolean alreadyHasQueryParams = connectionString.contains("?");
113-
connectionString += (alreadyHasQueryParams ? "&" : "?") + "authMechanismProperties="
114-
+ mongoClientConfig.credentials().authMechanismProperties().entrySet().stream()
115-
.map(prop -> prop.getKey() + ":" + prop.getValue()).collect(Collectors.joining(","));
124+
if (liquibaseMongodbConfig.liquibaseTablespaceName().isPresent()) {
125+
database.setLiquibaseTablespaceName(liquibaseMongodbConfig.liquibaseTablespaceName().get());
116126
}
117-
118-
Database database = DatabaseFactory.getInstance().openDatabase(connectionString,
119-
this.mongoClientConfig.credentials().username().orElse(null),
120-
this.mongoClientConfig.credentials().password().orElse(null),
121-
null, resourceAccessor);
122-
123-
if (database != null) {
124-
liquibaseMongodbConfig.liquibaseCatalogName().ifPresent(database::setLiquibaseCatalogName);
125-
liquibaseMongodbConfig.liquibaseSchemaName().ifPresent(database::setLiquibaseSchemaName);
126-
liquibaseMongodbConfig.liquibaseTablespaceName().ifPresent(database::setLiquibaseTablespaceName);
127-
128-
if (liquibaseMongodbConfig.defaultCatalogName().isPresent()) {
129-
database.setDefaultCatalogName(liquibaseMongodbConfig.defaultCatalogName().get());
130-
}
131-
if (liquibaseMongodbConfig.defaultSchemaName().isPresent()) {
132-
database.setDefaultSchemaName(liquibaseMongodbConfig.defaultSchemaName().get());
133-
}
127+
if (liquibaseMongodbConfig.defaultCatalogName().isPresent()) {
128+
database.setDefaultCatalogName(liquibaseMongodbConfig.defaultCatalogName().get());
129+
}
130+
if (liquibaseMongodbConfig.defaultSchemaName().isPresent()) {
131+
database.setDefaultSchemaName(liquibaseMongodbConfig.defaultSchemaName().get());
134132
}
135133
Liquibase liquibase = new Liquibase(parsedChangeLog, resourceAccessor, database);
136134

@@ -145,6 +143,16 @@ public Liquibase createLiquibase() {
145143
}
146144
}
147145

146+
private Database createDatabase(MongoClients clients, String clientName, String databaseName) {
147+
MongoConnection databaseConnection = new MongoConnection();
148+
MongoClient mongoClient = clients.createMongoClient(clientName);
149+
databaseConnection.setMongoClient(mongoClient);
150+
databaseConnection.setMongoDatabase(mongoClient.getDatabase(databaseName));
151+
Database database = new MongoLiquibaseDatabase();
152+
database.setConnection(databaseConnection);
153+
return database;
154+
}
155+
148156
public LiquibaseMongodbConfig getConfiguration() {
149157
return liquibaseMongodbConfig;
150158
}

Diff for: extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbConfig.java

+5
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public interface LiquibaseMongodbConfig {
2323
@WithDefault("true")
2424
boolean enabled();
2525

26+
/**
27+
* Mongodb client name to use to connect to database, defaults to the default mongodb client.
28+
*/
29+
Optional<String> mongoClientName();
30+
2631
/**
2732
* The migrate at start flag
2833
*/

Diff for: extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public Supplier<LiquibaseMongodbFactory> liquibaseSupplier(LiquibaseMongodbConfi
2828
return new Supplier<LiquibaseMongodbFactory>() {
2929
@Override
3030
public LiquibaseMongodbFactory get() {
31-
return new LiquibaseMongodbFactory(config, buildTimeConfig, mongodbConfig.defaultMongoClientConfig());
31+
return new LiquibaseMongodbFactory(config, buildTimeConfig, mongodbConfig);
3232
}
3333
};
3434
}

Diff for: integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/Fruit.java

+2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.quarkus.it.liquibase.mongodb;
22

33
import io.quarkus.mongodb.panache.PanacheMongoEntity;
4+
import io.quarkus.mongodb.panache.common.MongoEntity;
45

6+
@MongoEntity(clientName = "fruit-client")
57
public class Fruit extends PanacheMongoEntity {
68
public String name;
79
public String color;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
quarkus.mongodb.connection-string=mongodb://localhost:27017
2-
quarkus.mongodb.database=fruits
1+
# The tests use flapdoodle no need for devservices
2+
quarkus.mongodb.devservices.enabled=false
3+
34
quarkus.liquibase-mongodb.change-log=liquibase/changelog.xml
4-
quarkus.liquibase-mongodb.migrate-at-start=true
5+
quarkus.liquibase-mongodb.migrate-at-start=true
6+
quarkus.liquibase-mongodb.mongo-client-name=fruit-client
7+
quarkus.mongodb.fruit-client.database=fruits
8+
quarkus.mongodb.fruit-client.hosts=localhost:27018

Diff for: integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/FruitResourceTest.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.stream.StreamSupport;
99

1010
import jakarta.inject.Inject;
11+
import jakarta.inject.Named;
1112

1213
import org.bson.Document;
1314
import org.junit.jupiter.api.Assertions;
@@ -19,22 +20,24 @@
1920
import com.mongodb.client.MongoClient;
2021

2122
import io.quarkus.test.common.QuarkusTestResource;
23+
import io.quarkus.test.common.ResourceArg;
2224
import io.quarkus.test.junit.QuarkusTest;
2325
import io.quarkus.test.mongodb.MongoTestResource;
2426
import io.restassured.common.mapper.TypeRef;
2527

2628
@QuarkusTest
27-
@QuarkusTestResource(MongoTestResource.class)
29+
@QuarkusTestResource(value = MongoTestResource.class, initArgs = @ResourceArg(name = "port", value = "27018"))
2830
@DisabledOnOs(OS.WINDOWS)
2931
class FruitResourceTest {
3032

3133
@Inject
34+
@Named("fruit-client")
3235
MongoClient mongoClient;
3336

3437
@Test
3538
public void testTheEndpoint() {
3639
// assert that a fruit exist as one has been created in the changelog
37-
List<Fruit> list = get("/fruits").as(new TypeRef<List<Fruit>>() {
40+
List<Fruit> list = get("/fruits").as(new TypeRef<>() {
3841
});
3942
Assertions.assertEquals(1, list.size());
4043
}

Diff for: integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/NativeFruitResourceTestIT.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
import org.junit.jupiter.api.condition.OS;
1111

1212
import io.quarkus.test.common.QuarkusTestResource;
13+
import io.quarkus.test.common.ResourceArg;
1314
import io.quarkus.test.junit.QuarkusIntegrationTest;
1415
import io.quarkus.test.mongodb.MongoTestResource;
1516
import io.restassured.common.mapper.TypeRef;
1617

1718
@QuarkusIntegrationTest
18-
@QuarkusTestResource(MongoTestResource.class)
19+
@QuarkusTestResource(value = MongoTestResource.class, initArgs = @ResourceArg(name = "port", value = "27018"))
1920
@DisabledOnOs(OS.WINDOWS)
2021
class NativeFruitResourceTestIT {
2122
@Test

0 commit comments

Comments
 (0)