Skip to content

Commit

Permalink
DBAAS-42: Added all the properties for MongoDB. Fixed an issue with t…
Browse files Browse the repository at this point in the history
…railing slash before options + handling host and port in one property. Setting Quarkus user/pass separately from connection string.
  • Loading branch information
sgahlot committed Aug 20, 2021
1 parent 0fe4312 commit 58d4bfd
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.mongodb.runtime;

import static java.lang.Boolean.parseBoolean;
import static java.lang.String.format;

import java.util.HashMap;
Expand All @@ -17,49 +18,53 @@
* ServiceBindingConverter for MongoDB to support SBO (Service Binding Operator) in Quarkus.
*
* This class supports both the <b>Standard</b> and <b>SRV</b> connection string format for
* MongoDB depending on whether port is provided or not.
* MongoDB depending on whether <b>srv</b> property is true or false. If the srv property is
* missing then it is same as having a value of false.
*
* <br>
* <br>
* Following individual properties are supported to make the connection string:
* <ul>
* <li>username</li>
* <li>password</li>
* <li>host</li>
* <li>port</li>
* <li>host (<i>port information can be provided in this property separated by : sign</i>, e.g. localhost:27010</li>
* <li>database</li>
* <li>srv</li>
* <li>options</li>
* </ul>
* <i>Other than host all other properties are optinoal</i>
* <i>Other than host all other properties are optional</i>
*
* <br>
* <br>
* Only following options are supported by default:
* The Quarkus properties set by this class are:
* <ul>
* <li>retryWrites=true</li>
* <li>w=majority</li>
* <li>quarkus.mongodb.connection-string</li>
* <li>quarkus.mongodb.credentials.username (<i>if username is provided</i>)</li>
* <li>quarkus.mongodb.credentials.password (<i>if password is provided</i>)</li>
* </ul>
*
*/
public class MongoServiceBindingConverter implements ServiceBindingConverter {
private static final Logger LOGGER = Logger.getLogger(MongoServiceBindingConverter.class);

private static final String BINDING_TYPE = "mongodb";
public static final String BINDING_CONFIG_SOURCE_NAME = BINDING_TYPE + "-k8s-service-binding-source";
public static final String MONGO_DB_CONNECTION_STRING = "quarkus.mongodb.connection-string";
public static final String MONGO_DB_USERNAME = "quarkus.mongodb.credentials.username";
public static final String MONGO_DB_PASSWORD = "quarkus.mongodb.credentials.password";

// DB properties
public static final String DB_USER = "username";
public static final String DB_PASSWORD = "password";
public static final String DB_HOST = "host";
public static final String DB_PORT = "port";
public static final String DB_DATABASE = "database";
public static final String DB_OPTIONS = "options";
public static final String DB_PREFIX_STANDARD = "mongodb://";
public static final String DB_PREFIX_SRV = "mongodb+srv://";
public static final String DB_DEFAULT_OPTIONS = "?retryWrites=true&w=majority";
public static final String DB_SRV = "srv";

private static final String CONNECTION_STRING_WITH_USER = "%s%s:%s@%s/%s" + DB_DEFAULT_OPTIONS; // user:pass@hostPort/database
private static final String CONNECTION_STRING_WITH_USER_NO_DB = "%s%s:%s@%s" + DB_DEFAULT_OPTIONS; // user:pass@hostPort
private static final String CONNECTION_STRING_WITHOUT_USER = "%s%s/%s" + DB_DEFAULT_OPTIONS; // hostPort/database
private static final String CONNECTION_STRING_WITHOUT_USER_NO_DB = "%s%s" + DB_DEFAULT_OPTIONS; // hostPort
// 1st format specifier is for the prefix in each of the following ConnectionString
private static final String CONNECTION_STRING_WITH_HOST_N_DB = "%s%s/%s"; // hostPort/database
private static final String CONNECTION_STRING_WITH_ONLY_HOST = "%s%s"; // hostPort

@Override
public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> serviceBindings) {
Expand All @@ -68,37 +73,54 @@ public Optional<ServiceBindingConfigSource> convert(List<ServiceBinding> service
return Optional.empty();
}

ServiceBinding binding = matchingByType.get();
Map<String, String> properties = new HashMap<>();

properties.put(MONGO_DB_CONNECTION_STRING, getConnectionString(matchingByType.get()));
setConnectionString(binding, properties);
setUsername(binding, properties);
setPassword(binding, properties);

return Optional.of(new ServiceBindingConfigSource(BINDING_CONFIG_SOURCE_NAME, properties));
}

private String getConnectionString(ServiceBinding binding) {
String user = getDbProperty(binding, DB_USER);
String password = getDbProperty(binding, DB_PASSWORD);
String hostPort = getHostPort(binding);
private void setConnectionString(ServiceBinding binding, Map<String, String> properties) {
String hostPort = getHost(binding);
String prefix = isSrv(binding) ? DB_PREFIX_SRV : DB_PREFIX_STANDARD;
String database = getDbProperty(binding, DB_DATABASE);
String prefix = isPortProvided(hostPort) ? DB_PREFIX_STANDARD : DB_PREFIX_SRV;

String options = getDbProperty(binding, DB_OPTIONS);
boolean isOptionsNotBlank = isNotBlank(options);
String connectionString;
if (isBlank(user)) {
if (isBlank(database)) {
connectionString = format(CONNECTION_STRING_WITHOUT_USER_NO_DB, prefix, hostPort);
} else {
connectionString = format(CONNECTION_STRING_WITHOUT_USER, prefix, hostPort, database);

if (isBlank(database)) {
connectionString = format(CONNECTION_STRING_WITH_ONLY_HOST, prefix, hostPort);

if (isOptionsNotBlank) {
// We need a trailing slash before options otherwise Mongo throws "contains options without trailing slash" error
// If the database value is not present, then we haven't yet added the trailing slash (and hence add it here)
connectionString += "/";
}
} else {
if (isBlank(database)) {
connectionString = format(CONNECTION_STRING_WITH_USER_NO_DB, prefix, user, password, hostPort);
} else {
connectionString = format(CONNECTION_STRING_WITH_USER, prefix, user, password, hostPort, database);
}
connectionString = format(CONNECTION_STRING_WITH_HOST_N_DB, prefix, hostPort, database);
}

if (isOptionsNotBlank) {
connectionString += "?" + options;
}

LOGGER.info(format("MongoDB connection string: [%s]", connectionString));
return connectionString;

properties.put(MONGO_DB_CONNECTION_STRING, connectionString);
}

private void setUsername(ServiceBinding binding, Map<String, String> properties) {
String username = getDbProperty(binding, DB_USER); // encodeIfContainsSpecialCharacters(getDbProperty(binding, DB_USER));
LOGGER.info(format("MongoDB username=%s", username));

properties.put(MONGO_DB_USERNAME, username);
}

private void setPassword(ServiceBinding binding, Map<String, String> properties) {
properties.put(MONGO_DB_PASSWORD, getDbProperty(binding, DB_PASSWORD));
}

private String getDbProperty(ServiceBinding binding, String dbPropertyKey) {
Expand All @@ -110,24 +132,20 @@ private String getDbProperty(ServiceBinding binding, String dbPropertyKey) {
return dbPropertyValue;
}

private String getHostPort(ServiceBinding binding) {
private String getHost(ServiceBinding binding) {
String host = getDbProperty(binding, DB_HOST);
String port = getDbProperty(binding, DB_PORT);

String hostPort = host;
if (isNotBlank(host)) {
if (isNotBlank(port)) {
hostPort = host + ":" + port;
}
} else {
if (isBlank(host)) {
LOGGER.warn("Unable to get the host property. Connection string won't be correct");
}

return hostPort;
return host;
}

private boolean isPortProvided(String hostPort) {
return hostPort != null && hostPort.contains(":");
private boolean isSrv(ServiceBinding binding) {
String srv = getDbProperty(binding, DB_SRV);

return isNotBlank(srv) && parseBoolean(srv);
}

private boolean isBlank(String value) {
Expand Down
Loading

0 comments on commit 58d4bfd

Please sign in to comment.