Merge branch 'release' of github.com:appsmithorg/appsmith into release
This commit is contained in:
commit
926c6eb0fe
|
|
@ -1,5 +1,5 @@
|
|||
const ApiEditor = require("../../../locators/ApiEditor.json");
|
||||
const apiwidget = require("../../../locators/apiWidgetsLocator.json");
|
||||
const apiwidget = require("../../../locators/apiWidgetslocator.json");
|
||||
|
||||
describe("Test curl import flow", function() {
|
||||
it("Test curl import flow for POST action", function() {
|
||||
|
|
|
|||
|
|
@ -47,14 +47,18 @@ export const getWidgetPropsForPropertyPane = createSelector(
|
|||
const evaluatedWidget = _.find(evaluatedTree, {
|
||||
widgetId: widget.widgetId,
|
||||
}) as DataTreeWidget;
|
||||
const widgetProperties = {
|
||||
...widget,
|
||||
evaluatedValues: { ...evaluatedWidget.evaluatedValues },
|
||||
};
|
||||
if (evaluatedWidget.invalidProps) {
|
||||
const { invalidProps, validationMessages } = evaluatedWidget;
|
||||
widgetProperties.invalidProps = invalidProps;
|
||||
widgetProperties.validationMessages = validationMessages;
|
||||
const widgetProperties = { ...widget };
|
||||
if (evaluatedWidget) {
|
||||
if (evaluatedWidget.evaluatedValues) {
|
||||
widgetProperties.evaluatedValues = {
|
||||
...evaluatedWidget.evaluatedValues,
|
||||
};
|
||||
}
|
||||
if (evaluatedWidget.invalidProps) {
|
||||
const { invalidProps, validationMessages } = evaluatedWidget;
|
||||
widgetProperties.invalidProps = invalidProps;
|
||||
widgetProperties.validationMessages = validationMessages;
|
||||
}
|
||||
}
|
||||
return widgetProperties;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
plugin.id=mysql-plugin
|
||||
plugin.class=com.external.plugins.MysqlPlugin
|
||||
plugin.version=1.0-SNAPSHOT
|
||||
plugin.provider=tech@appsmith.com
|
||||
plugin.dependencies=
|
||||
121
app/server/appsmith-plugins/mysqlPlugin/pom.xml
Normal file
121
app/server/appsmith-plugins/mysqlPlugin/pom.xml
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.external.plugins</groupId>
|
||||
<artifactId>mysqlPlugin</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>mysqlPlugin</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>11</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<plugin.id>mysql-plugin</plugin.id>
|
||||
<plugin.class>com.external.plugins.MySqlPlugin</plugin.class>
|
||||
<plugin.version>1.0-SNAPSHOT</plugin.version>
|
||||
<plugin.provider>tech@appsmith.com</plugin.provider>
|
||||
<plugin.dependencies/>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.pf4j</groupId>
|
||||
<artifactId>pf4j-spring</artifactId>
|
||||
<version>0.6.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.appsmith</groupId>
|
||||
<artifactId>interfaces</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.20</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.13.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<version>1.14.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>3.2.11.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Plugin-Id>${plugin.id}</Plugin-Id>
|
||||
<Plugin-Class>${plugin.class}</Plugin-Class>
|
||||
<Plugin-Version>${plugin.version}</Plugin-Version>
|
||||
<Plugin-Provider>${plugin.provider}</Plugin-Provider>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
219
app/server/appsmith-plugins/mysqlPlugin/src/main/java/com/external/plugins/MySqlPlugin.java
vendored
Normal file
219
app/server/appsmith-plugins/mysqlPlugin/src/main/java/com/external/plugins/MySqlPlugin.java
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.models.*;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginError;
|
||||
import com.appsmith.external.pluginExceptions.AppsmithPluginException;
|
||||
import com.appsmith.external.plugins.BasePlugin;
|
||||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.pf4j.Extension;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.util.*;
|
||||
|
||||
import static com.appsmith.external.models.Connection.Mode.READ_ONLY;
|
||||
|
||||
public class MySqlPlugin extends BasePlugin {
|
||||
|
||||
static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
|
||||
|
||||
private static final String USER = "user";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
public MySqlPlugin(PluginWrapper wrapper) {
|
||||
super(wrapper);
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
@Extension
|
||||
public static class MySqlPluginExecutor implements PluginExecutor {
|
||||
|
||||
private Mono<Object> pluginErrorMono(Object... args) {
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> execute(Object connection, DatasourceConfiguration datasourceConfiguration,
|
||||
ActionConfiguration actionConfiguration) {
|
||||
|
||||
Connection conn = (Connection) connection;
|
||||
|
||||
String query = actionConfiguration.getBody();
|
||||
|
||||
if (query == null) {
|
||||
return pluginErrorMono("Missing required parameter: Query.");
|
||||
}
|
||||
|
||||
List<Map<String, Object>> rowsList = new ArrayList<>(50);
|
||||
|
||||
Statement statement = null;
|
||||
ResultSet resultSet = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
boolean isResultSet = statement.execute(query);
|
||||
|
||||
if (isResultSet) {
|
||||
resultSet = statement.getResultSet();
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
int colCount = metaData.getColumnCount();
|
||||
while (resultSet.next()) {
|
||||
Map<String, Object> row = new HashMap<>(colCount);
|
||||
for (int i = 1; i <= colCount; i++) {
|
||||
row.put(metaData.getColumnName(i), resultSet.getObject(i));
|
||||
}
|
||||
rowsList.add(row);
|
||||
}
|
||||
|
||||
} else {
|
||||
rowsList.add(Map.of("affectedRows", statement.getUpdateCount()));
|
||||
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
return pluginErrorMono(e.getMessage());
|
||||
|
||||
} finally {
|
||||
if (resultSet != null) {
|
||||
try {
|
||||
resultSet.close();
|
||||
} catch (SQLException e) {
|
||||
log.warn("Error closing MySql ResultSet", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (SQLException e) {
|
||||
log.warn("Error closing MySql Statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ActionExecutionResult result = new ActionExecutionResult();
|
||||
result.setBody(objectMapper.valueToTree(rowsList));
|
||||
result.setIsExecutionSuccess(true);
|
||||
log.debug("In the MySqlPlugin, got action execution result: " + result.toString());
|
||||
return Mono.just(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
||||
try {
|
||||
Class.forName(JDBC_DRIVER);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return pluginErrorMono("Error loading MySql JDBC Driver class.");
|
||||
}
|
||||
|
||||
String url;
|
||||
AuthenticationDTO authentication = datasourceConfiguration.getAuthentication();
|
||||
|
||||
com.appsmith.external.models.Connection configurationConnection = datasourceConfiguration.getConnection();
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(Map.of(
|
||||
USER, authentication.getUsername(),
|
||||
PASSWORD, authentication.getPassword()
|
||||
// TODO: Set SSL connection parameters.
|
||||
));
|
||||
|
||||
if (CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
|
||||
url = datasourceConfiguration.getUrl();
|
||||
|
||||
} else {
|
||||
StringBuilder urlBuilder = new StringBuilder("jdbc:mysql://");
|
||||
for (Endpoint endpoint : datasourceConfiguration.getEndpoints()) {
|
||||
urlBuilder
|
||||
.append(endpoint.getHost())
|
||||
.append(':')
|
||||
.append(ObjectUtils.defaultIfNull(endpoint.getPort(), 3306L))
|
||||
.append('/');
|
||||
|
||||
if (!StringUtils.isEmpty(authentication.getDatabaseName())) {
|
||||
urlBuilder.append(authentication.getDatabaseName());
|
||||
}
|
||||
}
|
||||
url = urlBuilder.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
Connection connection = DriverManager.getConnection(url, properties);
|
||||
connection.setReadOnly(
|
||||
configurationConnection != null && READ_ONLY.equals(configurationConnection.getMode()));
|
||||
return Mono.just(connection);
|
||||
} catch (SQLException e) {
|
||||
return pluginErrorMono("Error connecting to MySql.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void datasourceDestroy(Object connection) {
|
||||
Connection conn = (Connection) connection;
|
||||
try {
|
||||
if (conn != null) {
|
||||
conn.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Error closing MySQL Connection.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> validateDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
|
||||
Set<String> invalids = new HashSet<>();
|
||||
|
||||
if (datasourceConfiguration.getConnection() != null
|
||||
&& datasourceConfiguration.getConnection().getMode() == null) {
|
||||
invalids.add("Missing Connection Mode.");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(datasourceConfiguration.getUrl()) &&
|
||||
CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
|
||||
invalids.add("Missing endpoint and url");
|
||||
}
|
||||
|
||||
if (datasourceConfiguration.getAuthentication() == null) {
|
||||
invalids.add("Missing authentication details.");
|
||||
} else {
|
||||
if (StringUtils.isEmpty(datasourceConfiguration.getAuthentication().getUsername())) {
|
||||
invalids.add("Missing username for authentication.");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(datasourceConfiguration.getAuthentication().getPassword())) {
|
||||
invalids.add("Missing password for authentication.");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(datasourceConfiguration.getAuthentication().getDatabaseName())) {
|
||||
invalids.add("Missing database name");
|
||||
}
|
||||
}
|
||||
|
||||
return invalids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
return datasourceCreate(datasourceConfiguration)
|
||||
.map(connection -> {
|
||||
try {
|
||||
if (connection != null) {
|
||||
((Connection) connection).close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.warn("Error closing MySQL connection that was made for testing.", e);
|
||||
}
|
||||
|
||||
return new DatasourceTestResult();
|
||||
})
|
||||
.onErrorResume(error -> Mono.just(new DatasourceTestResult(error.getMessage())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
{
|
||||
"form": [
|
||||
{
|
||||
"sectionName": "Connection",
|
||||
"id": 1,
|
||||
"children": [
|
||||
{
|
||||
"label": "Connection Mode",
|
||||
"configProperty": "datasourceConfiguration.connection.mode",
|
||||
"controlType": "DROP_DOWN",
|
||||
"isRequired": true,
|
||||
"initialValue": "READ_WRITE",
|
||||
"options": [
|
||||
{
|
||||
"label": "Read Only",
|
||||
"value": "READ_ONLY"
|
||||
},
|
||||
{
|
||||
"label": "Read / Write",
|
||||
"value": "READ_WRITE"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Host Address",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].host",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
},
|
||||
{
|
||||
"label": "Port",
|
||||
"configProperty": "datasourceConfiguration.endpoints[*].port",
|
||||
"dataType": "NUMBER",
|
||||
"controlType": "KEYVALUE_ARRAY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Database Name",
|
||||
"configProperty": "datasourceConfiguration.authentication.databaseName",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"placeholderText": "Database name",
|
||||
"initialValue": "admin"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": "Authentication",
|
||||
"id": 2,
|
||||
"children": [
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Username",
|
||||
"configProperty": "datasourceConfiguration.authentication.username",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"placeholderText": "Username"
|
||||
},
|
||||
{
|
||||
"label": "Password",
|
||||
"configProperty": "datasourceConfiguration.authentication.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"placeholderText": "Password"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"sectionName": "SSL (optional)",
|
||||
"children": [
|
||||
{
|
||||
"label": "SSL Mode",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.authType",
|
||||
"controlType": "DROP_DOWN",
|
||||
"options": [
|
||||
{
|
||||
"label": "No SSL",
|
||||
"value": "NO_SSL"
|
||||
},
|
||||
{
|
||||
"label": "Allow",
|
||||
"value": "ALLOW"
|
||||
},
|
||||
{
|
||||
"label": "Prefer",
|
||||
"value": "PREFER"
|
||||
},
|
||||
{
|
||||
"label": "Require",
|
||||
"value": "REQUIRE"
|
||||
},
|
||||
{
|
||||
"label": "Disable",
|
||||
"value": "DISABLE"
|
||||
},
|
||||
{
|
||||
"label": "Verify-CA",
|
||||
"value": "VERIFY_CA"
|
||||
},
|
||||
{
|
||||
"label": "Verify-Full",
|
||||
"value": "VERIFY_FULL"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "Key File",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.keyFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.certificateFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sectionName": null,
|
||||
"children": [
|
||||
{
|
||||
"label": "CA Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.caCertificateFile",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "PEM Certificate",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.pemCertificate.file",
|
||||
"controlType": "FILE_PICKER"
|
||||
},
|
||||
{
|
||||
"label": "PEM Passphrase",
|
||||
"configProperty": "datasourceConfiguration.connection.ssl.pemCertificate.password",
|
||||
"dataType": "PASSWORD",
|
||||
"controlType": "INPUT_TEXT",
|
||||
"placeholderText": "PEM Passphrase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO users
|
||||
(id, name, gender, avatar, email, address, role)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?);
|
||||
|
|
@ -0,0 +1 @@
|
|||
DELETE FROM users WHERE id = ?;
|
||||
|
|
@ -0,0 +1 @@
|
|||
SELECT * FROM users ORDER BY id LIMIT 10;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE users
|
||||
SET status = 'APPROVED'
|
||||
WHERE id = 1;
|
||||
143
app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java
vendored
Normal file
143
app/server/appsmith-plugins/mysqlPlugin/src/test/java/com/external/plugins/MySqlPluginTest.java
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package com.external.plugins;
|
||||
|
||||
import com.appsmith.external.models.*;
|
||||
import lombok.extern.log4j.Log4j;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Log4j
|
||||
public class MySqlPluginTest {
|
||||
|
||||
MySqlPlugin.MySqlPluginExecutor pluginExecutor = new MySqlPlugin.MySqlPluginExecutor();
|
||||
|
||||
@ClassRule
|
||||
public static MySQLContainer mySQLContainer = new MySQLContainer()
|
||||
.withUsername("mysql").withPassword("password").withDatabaseName("mysql");
|
||||
|
||||
String address;
|
||||
Integer port;
|
||||
String username, password;
|
||||
|
||||
DatasourceConfiguration dsConfig;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
address = mySQLContainer.getContainerIpAddress();
|
||||
port = mySQLContainer.getFirstMappedPort();
|
||||
username = mySQLContainer.getUsername();
|
||||
password = mySQLContainer.getPassword();
|
||||
createDatasourceConfiguration();
|
||||
}
|
||||
|
||||
private DatasourceConfiguration createDatasourceConfiguration() {
|
||||
AuthenticationDTO authDTO = new AuthenticationDTO();
|
||||
authDTO.setAuthType(AuthenticationDTO.Type.USERNAME_PASSWORD);
|
||||
authDTO.setUsername(username);
|
||||
authDTO.setPassword(password);
|
||||
authDTO.setDatabaseName("mysql");
|
||||
|
||||
Endpoint endpoint = new Endpoint();
|
||||
endpoint.setHost(address);
|
||||
endpoint.setPort(port.longValue());
|
||||
|
||||
dsConfig = new DatasourceConfiguration();
|
||||
dsConfig.setAuthentication(authDTO);
|
||||
dsConfig.setEndpoints(List.of(endpoint));
|
||||
return dsConfig;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectMySQLContainer() {
|
||||
|
||||
Mono<Object> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
|
||||
|
||||
StepVerifier.create(dsConnectionMono)
|
||||
.assertNext(connection -> {
|
||||
java.sql.Connection conn = (Connection) connection;
|
||||
assertNotNull(conn);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute() {
|
||||
Mono<Object> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
|
||||
|
||||
ActionConfiguration actionConfiguration = new ActionConfiguration();
|
||||
actionConfiguration.setBody("show databases");
|
||||
|
||||
Mono<Object> executeMono = dsConnectionMono.flatMap(conn -> pluginExecutor.execute(conn, dsConfig, actionConfiguration));
|
||||
|
||||
StepVerifier.create(executeMono)
|
||||
.assertNext(obj -> {
|
||||
ActionExecutionResult result = (ActionExecutionResult) obj;
|
||||
System.out.println(result);
|
||||
assertNotNull(result);
|
||||
assertTrue(result.getIsExecutionSuccess());
|
||||
assertNotNull(result.getBody());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDatasourceNullCredentials() {
|
||||
dsConfig.setConnection(new com.appsmith.external.models.Connection());
|
||||
dsConfig.getAuthentication().setUsername(null);
|
||||
dsConfig.getAuthentication().setPassword(null);
|
||||
dsConfig.getAuthentication().setDatabaseName("someDbName");
|
||||
Set<String> output = pluginExecutor.validateDatasource(dsConfig);
|
||||
assertTrue(output.contains("Missing username for authentication."));
|
||||
assertTrue(output.contains("Missing password for authentication."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDatasourceMissingDBName() {
|
||||
dsConfig.getAuthentication().setDatabaseName("");
|
||||
Set<String> output = pluginExecutor.validateDatasource(dsConfig);
|
||||
assertEquals(output.size(), 1);
|
||||
assertTrue(output.contains("Missing database name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateDatasourceNullEndpoint() {
|
||||
dsConfig.setEndpoints(null);
|
||||
Set<String> output = pluginExecutor.validateDatasource(dsConfig);
|
||||
assertEquals(output.size(), 1);
|
||||
assertTrue(output.contains("Missing endpoint and url"));
|
||||
}
|
||||
|
||||
/* checking that the connection is being closed after the datadourceDestroy method is being called
|
||||
NOT : this test case will fail in case of a SQL Exception
|
||||
*/
|
||||
@Test
|
||||
public void testDatasourceDestroy() {
|
||||
|
||||
Mono<Object> connectionMono = pluginExecutor.datasourceCreate(dsConfig);
|
||||
|
||||
StepVerifier.create(connectionMono)
|
||||
.assertNext(connection -> {
|
||||
java.sql.Connection conn = (Connection) connection;
|
||||
pluginExecutor.datasourceDestroy(conn);
|
||||
try {
|
||||
assertEquals(conn.isClosed(), true);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
<module>restApiPlugin</module>
|
||||
<module>mongoPlugin</module>
|
||||
<module>rapidApiPlugin</module>
|
||||
<module>mysqlPlugin</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
@ -2,4 +2,4 @@ plugin.id=postgres-plugin
|
|||
plugin.class=com.external.plugins.PostgresPlugin
|
||||
plugin.version=1.0-SNAPSHOT
|
||||
plugin.provider=tech@appsmith.com
|
||||
plugin.dependencies=
|
||||
plugin.dependencies=
|
||||
|
|
@ -491,4 +491,35 @@ public class DatabaseChangelog {
|
|||
mongoTemplate.save(datasource);
|
||||
}
|
||||
}
|
||||
|
||||
@ChangeSet(order = "018", id = "install-mysql-plugins", author = "")
|
||||
public void mysqlPlugin(MongoTemplate mongoTemplate) {
|
||||
Plugin plugin1 = new Plugin();
|
||||
plugin1.setName("Mysql");
|
||||
plugin1.setType(PluginType.DB);
|
||||
plugin1.setPackageName("mysql-plugin");
|
||||
plugin1.setUiComponent("DbEditorForm");
|
||||
plugin1.setIconLocation("https://s3.us-east-2.amazonaws.com/assets.appsmith.com/Mysql.png");
|
||||
plugin1.setDefaultInstall(true);
|
||||
try {
|
||||
mongoTemplate.insert(plugin1);
|
||||
} catch (DuplicateKeyException e) {
|
||||
log.warn("mysql-plugin already present in database.");
|
||||
}
|
||||
|
||||
for (Organization organization : mongoTemplate.findAll(Organization.class)) {
|
||||
if (CollectionUtils.isEmpty(organization.getPlugins())) {
|
||||
organization.setPlugins(new ArrayList<>());
|
||||
}
|
||||
|
||||
final Set<String> installedPlugins = organization.getPlugins()
|
||||
.stream().map(OrganizationPlugin::getPluginId).collect(Collectors.toSet());
|
||||
|
||||
if (!installedPlugins.contains(plugin1.getId())) {
|
||||
organization.getPlugins()
|
||||
.add(new OrganizationPlugin(plugin1.getId(), OrganizationPluginStatus.FREE));
|
||||
}
|
||||
mongoTemplate.save(organization);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ import reactor.core.publisher.Flux;
|
|||
@Repository
|
||||
public interface ApplicationRepository extends BaseRepository<Application, String>, CustomApplicationRepository {
|
||||
|
||||
Flux<Application> findByOrganizationId(String organizationId);
|
||||
Flux<Application> findByOrganizationIdAndIsPublicTrue(String organizationId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ public class ExamplesOrganizationCloner {
|
|||
private Mono<Void> cloneApplications(String fromOrganizationId, String toOrganizationId) {
|
||||
final Mono<Map<String, Datasource>> cloneDatasourcesMono = cloneDatasources(fromOrganizationId, toOrganizationId).cache();
|
||||
return applicationRepository
|
||||
.findByOrganizationId(fromOrganizationId)
|
||||
.findByOrganizationIdAndIsPublicTrue(fromOrganizationId)
|
||||
.flatMap(application -> {
|
||||
final String templateApplicationId = application.getId();
|
||||
makePristine(application);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.appsmith.server.solutions;
|
||||
|
||||
import com.appsmith.server.acl.AclPermission;
|
||||
import com.appsmith.server.constants.FieldName;
|
||||
import com.appsmith.server.domains.Application;
|
||||
import com.appsmith.server.domains.Config;
|
||||
import com.appsmith.server.domains.Datasource;
|
||||
import com.appsmith.server.domains.Organization;
|
||||
import com.appsmith.server.repositories.ConfigRepository;
|
||||
import com.appsmith.server.repositories.OrganizationRepository;
|
||||
import com.appsmith.server.services.ApplicationPageService;
|
||||
import com.appsmith.server.services.ApplicationService;
|
||||
import com.appsmith.server.services.DatasourceService;
|
||||
import com.appsmith.server.services.OrganizationService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.minidev.json.JSONObject;
|
||||
|
|
@ -44,9 +44,6 @@ public class ExamplesOrganizationClonerTests {
|
|||
@Autowired
|
||||
private ExamplesOrganizationCloner examplesOrganizationCloner;
|
||||
|
||||
@Autowired
|
||||
private OrganizationRepository organizationRepository;
|
||||
|
||||
@Autowired
|
||||
private ConfigRepository configRepository;
|
||||
|
||||
|
|
@ -56,22 +53,45 @@ public class ExamplesOrganizationClonerTests {
|
|||
@Autowired
|
||||
private DatasourceService datasourceService;
|
||||
|
||||
@Autowired
|
||||
private OrganizationService organizationService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationPageService applicationPageService;
|
||||
|
||||
@Test
|
||||
@WithUserDetails(value = "api_user")
|
||||
public void createNewUserValid() {
|
||||
final Mono<Organization> organizationMono = organizationRepository
|
||||
.findByName("Spring Test Organization", AclPermission.READ_ORGANIZATIONS)
|
||||
.flatMap(organization -> {
|
||||
if (organization.getId() == null) {
|
||||
log.error("Cannot find Spring Test Organization");
|
||||
}
|
||||
Config config = new Config();
|
||||
config.setName(ExamplesOrganizationCloner.TEMPLATE_ORGANIZATION_CONFIG_NAME);
|
||||
config.setConfig(new JSONObject(Map.of(FieldName.ORGANIZATION_ID, organization.getId())));
|
||||
return configRepository.save(config).thenReturn(organization);
|
||||
})
|
||||
.flatMap(organization -> examplesOrganizationCloner.cloneExamplesOrganization())
|
||||
.cache();
|
||||
public void cloneOrganizationWithItsContents() {
|
||||
|
||||
Organization newOrganization = new Organization();
|
||||
newOrganization.setName("Template Organization");
|
||||
final Mono<Organization> organizationMono = organizationService.create(newOrganization)
|
||||
.flatMap(organization -> {
|
||||
if (organization.getId() == null) {
|
||||
return Mono.error(new RuntimeException("Created templates organization doesn't have an ID."));
|
||||
}
|
||||
|
||||
Application app1 = new Application();
|
||||
app1.setName("1 - public app");
|
||||
app1.setOrganizationId(organization.getId());
|
||||
app1.setIsPublic(true);
|
||||
|
||||
Application app2 = new Application();
|
||||
app2.setOrganizationId(organization.getId());
|
||||
app2.setName("2 - private app");
|
||||
|
||||
Config config = new Config();
|
||||
config.setName(ExamplesOrganizationCloner.TEMPLATE_ORGANIZATION_CONFIG_NAME);
|
||||
config.setConfig(new JSONObject(Map.of(FieldName.ORGANIZATION_ID, organization.getId())));
|
||||
|
||||
return Mono.when(
|
||||
applicationPageService.createApplication(app1),
|
||||
applicationPageService.createApplication(app2),
|
||||
configRepository.save(config).thenReturn(organization)
|
||||
).thenReturn(organization);
|
||||
})
|
||||
.flatMap(organization -> examplesOrganizationCloner.cloneExamplesOrganization())
|
||||
.cache();
|
||||
|
||||
final Mono<Tuple3<Organization, List<Application>, List<Datasource>>> resultMono = Mono.zip(
|
||||
organizationMono,
|
||||
|
|
@ -90,12 +110,10 @@ public class ExamplesOrganizationClonerTests {
|
|||
assertThat(organization.getPolicies()).isNotEmpty();
|
||||
|
||||
final List<Application> applications = tuple.getT2();
|
||||
assertThat(applications).hasSize(3);
|
||||
assertThat(applications).hasSize(1);
|
||||
assertThat(applications.stream().map(Application::getName).collect(Collectors.toSet()))
|
||||
.containsExactlyInAnyOrder(
|
||||
"LayoutServiceTest TestApplications",
|
||||
"TestApplications",
|
||||
"Another TestApplications"
|
||||
"1 - public app"
|
||||
);
|
||||
|
||||
final List<Datasource> datasources = tuple.getT3();
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ if [[ -f .env ]]; then
|
|||
source .env
|
||||
fi
|
||||
|
||||
exec java -jar dist/server-*.jar
|
||||
(cd dist && exec java -jar server-*.jar)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user