Mongo Plugin works here. Barring the scenarios where the query json contains special characters in keys ($, .) it works properly.
This commit is contained in:
parent
0473e3d48e
commit
84837fe57f
|
|
@ -35,7 +35,16 @@ public class ActionConfiguration {
|
||||||
HttpMethod httpMethod;
|
HttpMethod httpMethod;
|
||||||
|
|
||||||
// DB action fields
|
// DB action fields
|
||||||
String query;
|
|
||||||
|
/*
|
||||||
|
* For SQL plugins, the query field would be of the following format :
|
||||||
|
* {
|
||||||
|
* "cmd" : "select * from users;"
|
||||||
|
* }
|
||||||
|
* For noSQL plugins, the query json would be constructed according to
|
||||||
|
* the requirements of the plugin.
|
||||||
|
*/
|
||||||
|
Map<String, Object> query;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Future plugins could require more fields that are not covered above.
|
* Future plugins could require more fields that are not covered above.
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,9 @@ public class ResourceConfiguration {
|
||||||
|
|
||||||
//For REST API
|
//For REST API
|
||||||
List<Property> headers;
|
List<Property> headers;
|
||||||
|
|
||||||
|
//This field is for plugins which allow the database name to be specified outside of the connection URL. The
|
||||||
|
//expectation from the plugins is that if the database name has been provided via this field, the database name
|
||||||
|
//should be according to this, else if database name is null, pick the database name from the URL.
|
||||||
|
String databaseName;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
84
app/server/appsmith-plugins/mongoPlugin/pom.xml
Normal file
84
app/server/appsmith-plugins/mongoPlugin/pom.xml
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?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>mongoPlugin</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>mongoPlugin</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>mongo-plugin</plugin.id>
|
||||||
|
<plugin.class>com.external.plugins.MongoPlugin</plugin.class>
|
||||||
|
<plugin.version>1.0-SNAPSHOT</plugin.version>
|
||||||
|
<plugin.provider>tech@appsmith.com</plugin.provider>
|
||||||
|
<plugin.dependencies/>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pf4j</groupId>
|
||||||
|
<artifactId>pf4j-spring</artifactId>
|
||||||
|
<version>0.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.appsmith</groupId>
|
||||||
|
<artifactId>interfaces</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.8</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mongodb</groupId>
|
||||||
|
<artifactId>mongo-java-driver</artifactId>
|
||||||
|
<version>3.11.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.1.2</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<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>
|
||||||
|
<Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>
|
||||||
|
</manifestEntries>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
130
app/server/appsmith-plugins/mongoPlugin/src/main/java/com/external/plugins/MongoPlugin.java
vendored
Normal file
130
app/server/appsmith-plugins/mongoPlugin/src/main/java/com/external/plugins/MongoPlugin.java
vendored
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
package com.external.plugins;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
|
import com.appsmith.external.models.ActionExecutionResult;
|
||||||
|
import com.appsmith.external.models.ResourceConfiguration;
|
||||||
|
import com.appsmith.external.plugins.BasePlugin;
|
||||||
|
import com.appsmith.external.plugins.PluginExecutor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.mongodb.MongoClient;
|
||||||
|
import com.mongodb.MongoClientURI;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.conversions.Bson;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.pf4j.Extension;
|
||||||
|
import org.pf4j.PluginWrapper;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MongoPlugin extends BasePlugin {
|
||||||
|
|
||||||
|
private static ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public MongoPlugin(PluginWrapper wrapper) {
|
||||||
|
super(wrapper);
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Extension
|
||||||
|
public static class MongoPluginExecutor implements PluginExecutor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For reference on creating the json queries for Mongo please head to
|
||||||
|
* https://docs.huihoo.com/mongodb/3.4/reference/command/index.html
|
||||||
|
*
|
||||||
|
* @param connection : This is the connection that is established to the data source. This connection is according
|
||||||
|
* to the parameters in Resource Configuration
|
||||||
|
* @param resourceConfiguration : These are the configurations which have been used to create a Resource from a Plugin
|
||||||
|
* @param actionConfiguration : These are the configurations which have been used to create an Action from a Resource.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<ActionExecutionResult> execute(Object connection,
|
||||||
|
ResourceConfiguration resourceConfiguration,
|
||||||
|
ActionConfiguration actionConfiguration) {
|
||||||
|
|
||||||
|
ActionExecutionResult result = new ActionExecutionResult();
|
||||||
|
MongoClient mongoClient = (MongoClient) connection;
|
||||||
|
if (mongoClient == null) {
|
||||||
|
return Mono.error(new Exception("Mongo Client is null."));
|
||||||
|
}
|
||||||
|
|
||||||
|
MongoClientURI mongoClientURI= new MongoClientURI(resourceConfiguration.getUrl());
|
||||||
|
|
||||||
|
String databaseName = resourceConfiguration.getDatabaseName();
|
||||||
|
if (databaseName == null) {
|
||||||
|
databaseName = mongoClientURI.getDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||||
|
|
||||||
|
Bson command = new Document(actionConfiguration.getQuery());
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
Document mongoOutput = database.runCommand(command);
|
||||||
|
|
||||||
|
JSONObject outputJson = new JSONObject(mongoOutput.toJson());
|
||||||
|
//The output json contains the key "ok". This is the status of the command
|
||||||
|
BigInteger status = outputJson.getBigInteger("ok");
|
||||||
|
JSONArray headerArray = new JSONArray();
|
||||||
|
if (BigInteger.ONE == status) {
|
||||||
|
//The json contains key "cursor" when find command was issued and there are 1 or more results. In case
|
||||||
|
//there are no results for find, this key is not present in the result json.
|
||||||
|
if (outputJson.has("cursor")) {
|
||||||
|
JSONArray outputResult = outputJson.getJSONObject("cursor").getJSONArray("firstBatch");
|
||||||
|
result.setBody(objectMapper.readTree(outputResult.toString()));
|
||||||
|
}
|
||||||
|
//The json contains key "n" when insert/update command is issued. "n" for update signifies the no of
|
||||||
|
//documents selected for update. "n" in case of insert signifies the number of documents inserted.
|
||||||
|
if (outputJson.has("n")) {
|
||||||
|
JSONObject body = new JSONObject().put("n", outputJson.getBigInteger("n"));
|
||||||
|
headerArray.put(body);
|
||||||
|
}
|
||||||
|
//The json key constains key "nModified" in case of update command. This signifies the no of
|
||||||
|
//documents updated.
|
||||||
|
if (outputJson.has("nModified")) {
|
||||||
|
JSONObject body = new JSONObject().put("nModified", outputJson.getBigInteger("nModified"));
|
||||||
|
headerArray.put(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO
|
||||||
|
* Go through all the possible fields that are returned in the output JSON and add all the fields
|
||||||
|
* that are important to the headerArray.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject statusJson = new JSONObject().put("ok", status);
|
||||||
|
headerArray.put(statusJson);
|
||||||
|
result.setHeaders(objectMapper.readTree(headerArray.toString()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mono.just(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resourceCreate(ResourceConfiguration resourceConfiguration) {
|
||||||
|
|
||||||
|
MongoClientURI mongoClientURI= new MongoClientURI(resourceConfiguration.getUrl());
|
||||||
|
return new MongoClient(mongoClientURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resourceDestroy(Object connection) {
|
||||||
|
MongoClient mongoClient = (MongoClient) connection;
|
||||||
|
if (mongoClient != null) {
|
||||||
|
mongoClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
18
app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginTest.java
vendored
Normal file
18
app/server/appsmith-plugins/mongoPlugin/src/test/java/com/external/plugins/MongoPluginTest.java
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.external.plugins;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for simple App.
|
||||||
|
*/
|
||||||
|
public class MongoPluginTest {
|
||||||
|
/**
|
||||||
|
* Rigorous Test :-)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldAnswerWithTrue() {
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
<modules>
|
<modules>
|
||||||
<module>postgresPlugin</module>
|
<module>postgresPlugin</module>
|
||||||
<module>restApiPlugin</module>
|
<module>restApiPlugin</module>
|
||||||
|
<module>mongoPlugin</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -2,14 +2,11 @@ package com.external.plugins;
|
||||||
|
|
||||||
import com.appsmith.external.models.ActionConfiguration;
|
import com.appsmith.external.models.ActionConfiguration;
|
||||||
import com.appsmith.external.models.ActionExecutionResult;
|
import com.appsmith.external.models.ActionExecutionResult;
|
||||||
import com.appsmith.external.models.Param;
|
|
||||||
import com.appsmith.external.models.ResourceConfiguration;
|
import com.appsmith.external.models.ResourceConfiguration;
|
||||||
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.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.pf4j.Extension;
|
import org.pf4j.Extension;
|
||||||
import org.pf4j.PluginException;
|
import org.pf4j.PluginException;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
|
|
@ -24,7 +21,7 @@ import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PostgresPlugin extends BasePlugin {
|
public class PostgresPlugin extends BasePlugin {
|
||||||
|
|
@ -38,6 +35,13 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Postgres plugin receives the query as json of the following format :
|
||||||
|
* {
|
||||||
|
* "cmd" : "select * from users;"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Extension
|
@Extension
|
||||||
public static class PostgresPluginExecutor implements PluginExecutor {
|
public static class PostgresPluginExecutor implements PluginExecutor {
|
||||||
|
|
@ -53,8 +57,9 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
ArrayList list = new ArrayList(50);
|
ArrayList list = new ArrayList(50);
|
||||||
try {
|
try {
|
||||||
Statement statement = conn.createStatement();
|
Statement statement = conn.createStatement();
|
||||||
|
Map<String, Object> queryJson = actionConfiguration.getQuery();
|
||||||
ResultSet resultSet = statement.executeQuery(actionConfiguration.getQuery());
|
String query = (String) queryJson.get("cmd");
|
||||||
|
ResultSet resultSet = statement.executeQuery(query);
|
||||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||||
Integer colCount = metaData.getColumnCount();
|
Integer colCount = metaData.getColumnCount();
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
|
|
@ -90,7 +95,8 @@ public class PostgresPlugin extends BasePlugin {
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
log.error("", e);
|
log.error("", e);
|
||||||
}
|
}
|
||||||
return conn;
|
// Connection wasn't created. Return null
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ public enum AppsmithError {
|
||||||
UNAUTHORIZED_ACCESS(401, 4002, "Unauthorized access"),
|
UNAUTHORIZED_ACCESS(401, 4002, "Unauthorized access"),
|
||||||
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request"),
|
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request"),
|
||||||
REPOSITORY_SAVE_FAILED(500, 5001, "Repository save failed."),
|
REPOSITORY_SAVE_FAILED(500, 5001, "Repository save failed."),
|
||||||
PLUGIN_INSTALLATION_FAILED_DOWNLOAD_ERROR(500, 5002, "Due to error in downloading the plugin from remote repository, plugin installation has failed. Check the jar location and try again.");
|
PLUGIN_INSTALLATION_FAILED_DOWNLOAD_ERROR(500, 5002, "Due to error in downloading the plugin from remote repository, plugin installation has failed. Check the jar location and try again."),
|
||||||
|
PLUGIN_RUN_FAILED(500, 5003, "Plugin execution failed with error {0}");
|
||||||
|
|
||||||
|
|
||||||
private Integer httpErrorCode;
|
private Integer httpErrorCode;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import com.appsmith.server.domains.Page;
|
||||||
import com.appsmith.server.domains.PageAction;
|
import com.appsmith.server.domains.PageAction;
|
||||||
import com.appsmith.server.domains.Plugin;
|
import com.appsmith.server.domains.Plugin;
|
||||||
import com.appsmith.server.domains.Resource;
|
import com.appsmith.server.domains.Resource;
|
||||||
import com.appsmith.server.domains.ResourceContext;
|
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.dtos.ExecuteActionDTO;
|
import com.appsmith.server.dtos.ExecuteActionDTO;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
|
@ -171,15 +170,15 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
|
||||||
resourceConfiguration = resource.getResourceConfiguration();
|
resourceConfiguration = resource.getResourceConfiguration();
|
||||||
actionConfiguration = action.getActionConfiguration();
|
actionConfiguration = action.getActionConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceContextService
|
return resourceContextService
|
||||||
.getResourceContext(resource.getId())
|
.getResourceContext(resource.getId())
|
||||||
//Now that we have the context (connection details, execute the action
|
//Now that we have the context (connection details, execute the action
|
||||||
.flatMap(resourceContext -> pluginExecutor.execute(
|
.flatMap(resourceContext -> pluginExecutor.execute(
|
||||||
resourceContext.getConnection(),
|
resourceContext.getConnection(),
|
||||||
resourceConfiguration,
|
resourceConfiguration,
|
||||||
actionConfiguration));
|
actionConfiguration));
|
||||||
}))
|
}))
|
||||||
|
.onErrorResume(e -> Mono.error(new AppsmithException(AppsmithError.PLUGIN_RUN_FAILED, e.getMessage())))
|
||||||
.flatMap(obj -> obj);
|
.flatMap(obj -> obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ url_allow = true {
|
||||||
p = op.permission
|
p = op.permission
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This is a global list of all the routes for all controllers. Any new controller that is written must
|
||||||
|
# carry an entry in this array. OPA performs ACL based on an intersection of these entries and permissions
|
||||||
|
# for a user + permissions inherited via the groups that the user is a part of.
|
||||||
allowed_operations = [
|
allowed_operations = [
|
||||||
{"method": "POST", "resource": "users", "permission": "create:users"},
|
{"method": "POST", "resource": "users", "permission": "create:users"},
|
||||||
{"method": "GET", "resource": "users", "permission": "read:users"},
|
{"method": "GET", "resource": "users", "permission": "read:users"},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user