Merge branch 'bug/datasource-api-validations' into 'release'

Postgres plugin load connection details from rich data structure instead of the URL

See merge request theappsmith/internal-tools-server!259
This commit is contained in:
Shrikant Kandula 2020-04-08 11:50:58 +00:00
commit d25b4acfd3

View File

@ -1,38 +1,39 @@
package com.external.plugins; package com.external.plugins;
import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.*;
import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.plugins.BasePlugin; import com.appsmith.external.plugins.BasePlugin;
import com.appsmith.external.plugins.PluginExecutor; import com.appsmith.external.plugins.PluginExecutor;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ObjectUtils;
import org.pf4j.Extension; import org.pf4j.Extension;
import org.pf4j.PluginException; import org.pf4j.PluginException;
import org.pf4j.PluginWrapper; import org.pf4j.PluginWrapper;
import org.springframework.util.Assert; import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.*;
import java.sql.ResultSet; import java.util.*;
import java.sql.ResultSetMetaData;
import java.sql.SQLException; import static com.appsmith.external.models.Connection.Mode.ReadOnly;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Slf4j @Slf4j
public class PostgresPlugin extends BasePlugin { public class PostgresPlugin extends BasePlugin {
private static ObjectMapper objectMapper; private static ObjectMapper objectMapper = new ObjectMapper();
static String JDBC_DRIVER = "org.postgresql.Driver"; static final String JDBC_DRIVER = "org.postgresql.Driver";
private static final String USER = "user";
private static final String PASSWORD = "password";
private static final String SSL = "ssl";
public PostgresPlugin(PluginWrapper wrapper) { public PostgresPlugin(PluginWrapper wrapper) {
super(wrapper); super(wrapper);
this.objectMapper = new ObjectMapper();
} }
/** /**
@ -47,58 +48,95 @@ public class PostgresPlugin extends BasePlugin {
public static class PostgresPluginExecutor implements PluginExecutor { public static class PostgresPluginExecutor implements PluginExecutor {
@Override @Override
public Mono<Object> execute(Object connection, public Mono<Object> execute(@NonNull Object connection,
DatasourceConfiguration datasourceConfiguration, DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) { ActionConfiguration actionConfiguration) {
Connection conn = (Connection) connection; Connection conn = (Connection) connection;
Assert.notNull(conn);
ArrayList list = new ArrayList(50); Map<String, Object> queryJson = actionConfiguration.getQuery();
String query = (String) queryJson.get("cmd");
if (query == null) {
return pluginErrorMono("Missing required parameter: Query.");
}
List<Map<String, Object>> rowsList = new ArrayList<>(50);
try { try {
Statement statement = conn.createStatement(); Statement statement = conn.createStatement();
Map<String, Object> queryJson = actionConfiguration.getQuery();
String query = (String) queryJson.get("cmd");
ResultSet resultSet = statement.executeQuery(query); ResultSet resultSet = statement.executeQuery(query);
ResultSetMetaData metaData = resultSet.getMetaData(); ResultSetMetaData metaData = resultSet.getMetaData();
Integer colCount = metaData.getColumnCount(); int colCount = metaData.getColumnCount();
while (resultSet.next()) { while (resultSet.next()) {
HashMap row = new HashMap(colCount); Map<String, Object> row = new HashMap<>(colCount);
for (int i = 1; i <= colCount; i++) { for (int i = 1; i <= colCount; i++) {
row.put(metaData.getColumnName(i), resultSet.getObject(i)); row.put(metaData.getColumnName(i), resultSet.getObject(i));
} }
list.add(row); rowsList.add(row);
} }
} catch (SQLException e) { } catch (SQLException e) {
log.error("", e); return pluginErrorMono(e);
} }
ActionExecutionResult result = new ActionExecutionResult(); ActionExecutionResult result = new ActionExecutionResult();
result.setBody(objectMapper.valueToTree(list)); result.setBody(objectMapper.valueToTree(rowsList));
result.setShouldCacheResponse(true); result.setShouldCacheResponse(true);
System.out.println("In the PostgresPlugin, got action execution result: " + result.toString()); System.out.println("In the PostgresPlugin, got action execution result: " + result.toString());
return Mono.just(result); return Mono.just(result);
} }
private Mono<Object> pluginErrorMono(Object... args) {
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, args));
}
@Override @Override
public Object datasourceCreate(DatasourceConfiguration datasourceConfiguration) { public Object datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
Connection conn = null;
try { try {
// Load the class into JVM
Class.forName(JDBC_DRIVER); Class.forName(JDBC_DRIVER);
// Create the connection
conn = DriverManager.getConnection(datasourceConfiguration.getUrl(),
datasourceConfiguration.getAuthentication().getUsername(),
datasourceConfiguration.getAuthentication().getPassword());
return conn;
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
log.error("", e); return pluginErrorMono("Error loading Postgres JDBC Driver class.");
} catch (SQLException e) { }
log.error("", e);
String url;
AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
Properties properties = new Properties();
properties.putAll(Map.of(
USER, authentication.getUsername(),
PASSWORD, authentication.getPassword(),
// TODO: Set SSL connection parameters.
SSL, datasourceConfiguration.getConnection().getSsl() != null
));
if (CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
url = datasourceConfiguration.getUrl();
} else {
StringBuilder urlBuilder = new StringBuilder("jdbc:postgresql://");
for (Endpoint endpoint : datasourceConfiguration.getEndpoints()) {
urlBuilder
.append(endpoint.getHost())
.append(':')
.append(ObjectUtils.defaultIfNull(endpoint.getPort(), 5432L))
.append('/')
.append(authentication.getDatabaseName());
}
url = urlBuilder.toString();
}
try {
Connection connection = DriverManager.getConnection(url, properties);
connection.setReadOnly(ReadOnly.equals(datasourceConfiguration.getConnection().getMode()));
return connection;
} catch (SQLException e) {
return pluginErrorMono("Error connecting to Postgres.");
} }
// Connection wasn't created. Return null
return null;
} }
@Override @Override
@ -109,17 +147,24 @@ public class PostgresPlugin extends BasePlugin {
conn.close(); conn.close();
} }
} catch (SQLException e) { } catch (SQLException e) {
log.error("", e); log.error("Error closing Postgres Connection.", e);
try {
throw new PluginException(e);
} catch (PluginException ex) {
ex.printStackTrace();
}
} }
} }
@SuppressWarnings("RedundantIfStatement")
@Override @Override
public Boolean isDatasourceValid(DatasourceConfiguration datasourceConfiguration) { public Boolean isDatasourceValid(@NonNull DatasourceConfiguration datasourceConfiguration) {
if (CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
return false;
}
if (datasourceConfiguration.getAuthentication() == null
|| datasourceConfiguration.getAuthentication().getUsername() == null
|| datasourceConfiguration.getAuthentication().getPassword() == null) {
return false;
}
return true; return true;
} }