Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue-66: Atlas cluster connection string #83

Merged
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
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
1. [Introduction](#introduction)
1. [Release Notes](#release-notes)
1. [Implemented Changes](#implemented-changes)
1. [Connection String Formats](#connection-string)
1. [Getting Started](#getting-started)
1. [Running tests](#running-tests)
1. [Integration](#integration)
Expand All @@ -28,9 +29,11 @@ Liquibase turned to be the most feasible tool to extend as it allows to define c
## Release Notes

#### 4.2.2.1
* Fixed [Issue-64:Support for DNS Seed List Connection Format or Atlas Cluster](https://github.com/liquibase/liquibase-mongodb/issues/66)
* Fixed [Issue-69: Does it support preconditions](https://github.com/liquibase/liquibase-mongodb/issues/69)
* Added DocumentExistsPrecondition, ExpectedDocumentCountPrecondition
* Fixed [Issue-74: createIndex with TTL (expireAfterSeconds) is ignored and normal index created](https://github.com/liquibase/liquibase-mongodb/issues/74)
* Fixed [Issue-79: CreateCollection silently drops supported options](https://github.com/liquibase/liquibase-mongodb/issues/79)

#### 4.2.2
* Support for Liquibase 4.2.2
Expand Down Expand Up @@ -101,6 +104,24 @@ Provides a helper to run specified database commands. This is the preferred meth
* [__adminCommand__](https://docs.mongodb.com/manual/reference/method/db.adminCommand/#db.adminCommand) -
Provides a helper to run specified database commands against the admin database

<a name="connection-string"></a>
## Connection String Formats

### [Standard Connection String Format](https://docs.mongodb.com/manual/reference/connection-string/index.html#standard-connection-string-format)

`
mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?replicaSet=mySet&authSource=authDB
`

### [DNS Seed List Connection Format](https://docs.mongodb.com/manual/reference/connection-string/index.html#dns-seed-list-connection-format)

`
mongodb+srv://[username:password@]host[/[database][?options]]
mongodb+srv://server.example.com/
mongodb+srv://:@cluster0.example.com/testdb?authSource=$external&authMechanism=MONGODB-AWS
`

<a name="getting-started"></a>
## Getting Started

Expand All @@ -115,6 +136,10 @@ mongo-java-driver:3.12.7
### Installing

* Clone the project

```shell
git clone https://github.com/liquibase/liquibase-mongodb
```
* [Run tests](#running-tests)

<a name="running-tests"></a>
Expand All @@ -128,7 +153,7 @@ Run Integration tests by enabling `run-its` profile

### Run integration tests

```shell script
```shell
mvn clean install -Prun-its
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
import com.mongodb.client.MongoClients;
import liquibase.Scope;
import liquibase.exception.DatabaseException;
import liquibase.util.StringUtil;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverPropertyInfo;
import java.util.Properties;
import java.util.logging.Logger;

import static java.util.Objects.nonNull;

public class MongoClientDriver implements Driver {

@Override
public Connection connect(String url, Properties info) {
public Connection connect(final String url, final Properties info) {
//Not applicable for non JDBC DBs
throw new UnsupportedOperationException("Cannot initiate a SQL Connection for a NoSql DB");
}
Expand All @@ -32,8 +35,9 @@ public MongoClient connect(final ConnectionString connectionString) throws Datab
}

@Override
public boolean acceptsURL(String url) {
return false;
public boolean acceptsURL(final String url) {
final String trimmedUrl = StringUtil.trimToEmpty(url);
return trimmedUrl.startsWith(MongoConnection.MONGO_DNS_PREFIX) || trimmedUrl.startsWith(MongoConnection.MONGO_PREFIX);
}

@Override
Expand Down
51 changes: 34 additions & 17 deletions src/main/java/liquibase/ext/mongodb/database/MongoConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,19 @@
import java.util.Optional;
import java.util.Properties;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.Optional.ofNullable;
import static liquibase.ext.mongodb.database.MongoLiquibaseDatabase.MONGODB_PRODUCT_SHORT_NAME;

@Getter
@Setter
@NoArgsConstructor
public class MongoConnection extends AbstractNoSqlConnection {

public static final int DEFAULT_PORT = 27017;
public static final String MONGO_PREFIX = MongoLiquibaseDatabase.MONGODB_PRODUCT_SHORT_NAME + "://";
public static final String MONGO_CONNECTION_STRING_PATTERN = "%s/%s";
public static final String MONGO_PREFIX = MONGODB_PRODUCT_SHORT_NAME + "://";
public static final String MONGO_DNS_PREFIX = MONGODB_PRODUCT_SHORT_NAME + "+srv://";

private ConnectionString connectionString;

Expand Down Expand Up @@ -80,24 +82,16 @@ public String getConnectionUserName() {
.map(MongoCredential::getUserName).orElse("");
}

@Override
public boolean isClosed() throws DatabaseException {
return isNull(client);
}

@Override
public void open(final String url, final Driver driverObject, final Properties driverProperties) throws DatabaseException {

try {

String urlWithCredentials = url;

if (nonNull(driverProperties)) {

final Optional<String> user = Optional.ofNullable(StringUtil.trimToNull(driverProperties.getProperty("user")));
final Optional<String> password = Optional.ofNullable(StringUtil.trimToNull(driverProperties.getProperty("password")));

if (user.isPresent() && password.isPresent()) {
// injects credentials
// mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database.collection][?options]]
urlWithCredentials = MONGO_PREFIX + user.get() + ":" + password.get() + "@" + StringUtil.trimToEmpty(url).replaceFirst(MONGO_PREFIX, "");
}
}
final String urlWithCredentials = injectCredentials(StringUtil.trimToEmpty(url), driverProperties);

this.connectionString = new ConnectionString(urlWithCredentials);

Expand All @@ -111,10 +105,33 @@ public void open(final String url, final Driver driverObject, final Properties d
}
}

private String injectCredentials(final String url, final Properties driverProperties) {

if (nonNull(driverProperties)) {

final Optional<String> user = Optional.ofNullable(StringUtil.trimToNull(driverProperties.getProperty("user")));
final Optional<String> password = Optional.ofNullable(StringUtil.trimToNull(driverProperties.getProperty("password")));

if (user.isPresent()) {
// injects credentials
// mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database.collection][?options]]
// mongodb+srv://[username:password@]host[:port1][?options]]
final String mongoPrefix = url.startsWith(MONGO_DNS_PREFIX) ? MONGO_DNS_PREFIX : MONGO_PREFIX;
return mongoPrefix + user.get() + password.map(p -> ":" + p).orElse("") + "@" +
url.substring(mongoPrefix.length());
}
}
return url;
}


@Override
public void close() throws DatabaseException {
try {
client.close();
if (!isClosed()) {
client.close();
client = null;
}
} catch (final Exception e) {
throw new DatabaseException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@
import liquibase.CatalogAndSchema;
import liquibase.Scope;
import liquibase.changelog.ChangeLogHistoryServiceFactory;
import liquibase.configuration.GlobalConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.ext.mongodb.configuration.MongoConfiguration;
import liquibase.nosql.database.AbstractNoSqlDatabase;
import liquibase.exception.LiquibaseException;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.ext.mongodb.configuration.MongoConfiguration;
import liquibase.ext.mongodb.statement.DropAllCollectionsStatement;
import liquibase.nosql.database.AbstractNoSqlDatabase;
import lombok.NoArgsConstructor;
import lombok.Setter;

Expand All @@ -41,6 +40,7 @@ public class MongoLiquibaseDatabase extends AbstractNoSqlDatabase {

public static final String MONGODB_PRODUCT_NAME = "MongoDB";
public static final String MONGODB_PRODUCT_SHORT_NAME = "mongodb";
public static final String ADMIN_DATABSE_NAME = "admin";

@Setter
private Boolean adjustTrackingTablesOnStartup;
Expand All @@ -58,7 +58,7 @@ public void dropDatabaseObjects(final CatalogAndSchema schemaToDrop) throws Liqu

@Override
public String getDefaultDriver(final String url) {
if (url.startsWith(MongoConnection.MONGO_PREFIX)) {
if (url.startsWith(MongoConnection.MONGO_DNS_PREFIX) || url.startsWith(MongoConnection.MONGO_PREFIX)) {
return MongoClientDriver.class.getName();
}
return null;
Expand Down Expand Up @@ -90,7 +90,7 @@ protected String getDefaultDatabaseProductName() {

@Override
public String getSystemSchema() {
return "admin";
return ADMIN_DATABSE_NAME;
}

/*********************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.exception.DatabaseException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand Down Expand Up @@ -51,8 +50,6 @@ public void setAutoCommit(boolean autoCommit) throws DatabaseException {

@Override
public String nativeSQL(String sql) {
//TODO: Investigate whether can be thrown not applicable
//throw new UnsupportedOperationException();
return null;
}

Expand Down Expand Up @@ -86,9 +83,4 @@ public void rollback() throws DatabaseException {
// Do nothing
}

@Override
public boolean isClosed() throws DatabaseException {
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package liquibase.ext.mongodb.database;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

@TestInstance(TestInstance.Lifecycle.PER_METHOD)
class MongoClientDriverTest {

protected MongoClientDriver mongoClientDriver;

@BeforeEach
void setUp() {
mongoClientDriver = new MongoClientDriver();
}

@Test
void acceptsURL() {
assertThat(mongoClientDriver.acceptsURL(null)).isFalse();
assertThat(mongoClientDriver.acceptsURL("")).isFalse();
assertThat(mongoClientDriver.acceptsURL("jdbc:oracle:thin:@localhost:1521:xe")).isFalse();
assertThat(mongoClientDriver.acceptsURL("mongodbsuffix://localhost")).isFalse();
assertThat(mongoClientDriver.acceptsURL("mongodb://localhost")).isTrue();
assertThat(mongoClientDriver.acceptsURL(" mongodb://localhost")).isTrue();
assertThat(mongoClientDriver.acceptsURL("mongodb+srv://localhost")).isTrue();
assertThat(mongoClientDriver.acceptsURL(" mongodb+srv://localhost")).isTrue();
}

@Test
void getMajorVersion() {
assertThat(mongoClientDriver.getMajorVersion()).isEqualTo(0);
}

@Test
void getMinorVersion() {
assertThat(mongoClientDriver.getMinorVersion()).isEqualTo(0);
}

@Test
void jdbcCompliant() {
assertThat(mongoClientDriver.jdbcCompliant()).isFalse();
}
}
Loading