Adding the executeAction API in the ActionController.

Also changed the interface for the PluginExecutor to include the resourceConfig and ActionConfig as well.
This commit is contained in:
Arpit Mohan 2019-09-17 12:18:23 +00:00
parent 16dca65a64
commit 0edc0f6363
28 changed files with 181 additions and 241 deletions

View File

@ -40,6 +40,24 @@
<version>3.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.5.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>

View File

@ -1,10 +1,10 @@
package com.appsmith.server.domains;
package com.appsmith.external.models;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import net.minidev.json.JSONObject;
import org.json.JSONObject;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.http.HttpMethod;
@ -15,7 +15,7 @@ import java.util.List;
@ToString
@NoArgsConstructor
@Document
public class ActionConfiguration extends BaseDomain {
public class ActionConfiguration {
/*
* Any of the fields mentioned below could be represented in mustache
* template. If the mustache template is found, it would be replaced

View File

@ -1,4 +1,4 @@
package com.appsmith.server.dtos;
package com.appsmith.external.models;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -15,4 +15,4 @@ public class AuthenticationDTO {
String authType;
String username;
String password;
}
}

View File

@ -1,4 +1,4 @@
package com.appsmith.server.domains;
package com.appsmith.external.models;
import lombok.Getter;
import lombok.Setter;

View File

@ -1,6 +1,5 @@
package com.appsmith.server.domains;
package com.appsmith.external.models;
import com.appsmith.server.dtos.AuthenticationDTO;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@ -14,7 +13,7 @@ import java.util.List;
@ToString
@NoArgsConstructor
@Document
public class ResourceConfiguration extends BaseDomain {
public class ResourceConfiguration {
String url;

View File

@ -1,10 +1,15 @@
package com.appsmith.external.plugins;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.CommandParams;
import com.appsmith.external.models.Param;
import com.appsmith.external.models.ResourceConfiguration;
import org.pf4j.ExtensionPoint;
import reactor.core.publisher.Flux;
import java.util.List;
public interface PluginExecutor extends ExtensionPoint {
Flux<Object> execute(String command, CommandParams params);
Flux<Object> execute(ResourceConfiguration resourceConfiguration, ActionConfiguration action, List<Param> params);
}

View File

@ -1,6 +1,9 @@
package com.external.plugins;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.CommandParams;
import com.appsmith.external.models.Param;
import com.appsmith.external.models.ResourceConfiguration;
import com.appsmith.external.plugins.BasePlugin;
import com.appsmith.external.plugins.PluginExecutor;
import lombok.extern.slf4j.Slf4j;
@ -10,6 +13,7 @@ import org.pf4j.PluginWrapper;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
@ -18,6 +22,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Slf4j
public class PostgresPlugin extends BasePlugin {
@ -70,15 +75,19 @@ public class PostgresPlugin extends BasePlugin {
public static class PostgresPluginExecutor implements PluginExecutor {
@Override
public Flux<Object> execute(String command, CommandParams commandParams) {
log.debug("In the PostgresPlugin execute with command: {}", command);
public Flux<Object> execute(ResourceConfiguration resourceConfiguration,
ActionConfiguration actionConfiguration,
List<Param> params) {
log.debug("In the PostgresPlugin execute with resourceConfiguration: {}, ActionConfig: {}",
resourceConfiguration, actionConfiguration);
Assert.notNull(conn);
ArrayList list = new ArrayList(50);
try {
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(command);
ResultSet resultSet = statement.executeQuery(actionConfiguration.getQuery());
ResultSetMetaData metaData = resultSet.getMetaData();
Integer colCount = metaData.getColumnCount();
while (resultSet.next()) {

View File

@ -2,9 +2,15 @@ package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Action;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.services.ActionService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping(Url.ACTION_URL)
@ -13,4 +19,9 @@ public class ActionController extends BaseController<ActionService, Action, Stri
public ActionController(ActionService service) {
super(service);
}
@PostMapping("/execute")
public Flux<Object> executeAction(@RequestBody ExecuteActionDTO executeActionDTO) {
return service.executeAction(executeActionDTO);
}
}

View File

@ -1,28 +0,0 @@
package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.services.QueryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping(Url.QUERY_URL)
public class QueryController extends BaseController<QueryService, Query, String> {
@Autowired
public QueryController(QueryService service) {
super(service);
}
@PostMapping("/execute/{name}")
public Flux<Object> executeQuery(@PathVariable String name, @RequestBody CommandQueryParams params) {
return service.executeQuery(name, params);
}
}

View File

@ -1,5 +1,6 @@
package com.appsmith.server.domains;
import com.appsmith.external.models.ActionConfiguration;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

View File

@ -0,0 +1,17 @@
package com.appsmith.server.domains;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OldProperty {
String key;
String value;
Boolean editable;
Boolean internal;
}

View File

@ -29,7 +29,7 @@ public class Query extends BaseDomain {
@Indexed(unique = true)
String name;
List<Property> properties;
List<OldProperty> properties;
List<Property> headers;
List<OldProperty> headers;
}

View File

@ -1,5 +1,6 @@
package com.appsmith.server.domains;
import com.appsmith.external.models.ResourceConfiguration;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

View File

@ -1,5 +1,6 @@
package com.appsmith.server.domains;
import com.appsmith.external.models.Property;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

View File

@ -8,7 +8,7 @@ import java.util.List;
@Getter
@Setter
public class CommandQueryParams {
List<Param> queryParams;
List<OldParam> queryOldParams;
List<Param> headerParams;
List<OldParam> headerOldParams;
}

View File

@ -0,0 +1,18 @@
package com.appsmith.server.dtos;
import com.appsmith.external.models.Param;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotNull;
import java.util.List;
@Getter
@Setter
public class ExecuteActionDTO {
@NotNull
String actionId;
List<Param> params;
}

View File

@ -0,0 +1,18 @@
package com.appsmith.server.dtos;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class OldAuthenticationDTO {
String authType;
String username;
String password;
}

View File

@ -5,7 +5,7 @@ import lombok.Setter;
@Getter
@Setter
public class Param {
public class OldParam {
String key;

View File

@ -1,88 +0,0 @@
package com.appsmith.server.plugins;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.services.OldPluginExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
@Slf4j
@Component
public class PostgresDBOldPluginExecutor extends OldPluginExecutor {
// JDBC driver name and database URL
@Value("${jdbc.postgres.driver}")
String JDBC_DRIVER;
@Value("${jdbc.postgres.url}")
String DB_URL;
// Database credentials
@Value("${jdbc.postgres.username}")
String DB_USER;
@Value("${jdbc.postgres.password}")
String DB_PASS;
Connection conn = null;
@Override
public Flux<Object> execute(Query queryObj, CommandQueryParams params) {
if (conn == null) {
init();
}
ArrayList list = new ArrayList(50);
try {
Statement statement = conn.createStatement();
String queryTemplate = queryObj.getCommandTemplate();
ResultSet resultSet = statement.executeQuery(queryTemplate);
ResultSetMetaData metaData = resultSet.getMetaData();
Integer colCount = metaData.getColumnCount();
while (resultSet.next()) {
HashMap row = new HashMap(colCount);
for (int i = 1; i <= colCount; i++) {
row.put(metaData.getColumnName(i), resultSet.getObject(i));
}
list.add(row);
}
} catch (SQLException e) {
e.printStackTrace();
}
return Flux.fromIterable(list);
}
@Override
public void init() {
log.debug("Going to initialize the PostgresDBPluginExecutor");
try {
// Load the class into JVM
Class.forName(JDBC_DRIVER);
log.debug("Got the jdbc url as {}", DB_URL);
// Create the connection
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
}
}

View File

@ -1,6 +1,6 @@
package com.appsmith.server.plugins;
import com.appsmith.server.domains.Property;
import com.appsmith.server.domains.OldProperty;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.services.OldPluginExecutor;
@ -30,9 +30,9 @@ public class RestTemplateOldPluginExecutor extends OldPluginExecutor {
@Override
protected Flux<Object> execute(Query query, CommandQueryParams params) {
String requestBody = query.getCommandTemplate();
Map<String, Property> propertyMap = query.getProperties()
Map<String, OldProperty> propertyMap = query.getProperties()
.stream()
.collect(Collectors.toMap(Property::getKey, prop -> prop));
.collect(Collectors.toMap(OldProperty::getKey, prop -> prop));
String url = propertyMap.get(PROP_URL).getValue();
String httpMethod = propertyMap.get(PROP_HTTP_METHOD).getValue();

View File

@ -2,7 +2,10 @@ package com.appsmith.server.repositories;
import com.appsmith.server.domains.Action;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface ActionRepository extends BaseRepository<Action, String> {
Mono<Action> findById(String id);
}

View File

@ -1,11 +0,0 @@
package com.appsmith.server.repositories;
import com.appsmith.server.domains.Query;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
@Repository
public interface QueryRepository extends BaseRepository<Query, String> {
Mono<Query> findByName(String name);
}

View File

@ -1,6 +1,11 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Action;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.dtos.ExecuteActionDTO;
import reactor.core.publisher.Flux;
public interface ActionService extends CrudService<Action, String> {
Flux<Object> executeAction(ExecuteActionDTO executeActionDTO);
}

View File

@ -1,18 +1,22 @@
package com.appsmith.server.services;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.domains.Action;
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.PageAction;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.Resource;
import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.ActionRepository;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
@ -29,6 +33,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
private final ResourceService resourceService;
private final PluginService pluginService;
private final PageService pageService;
private final PluginManager pluginManager;
@Autowired
public ActionServiceImpl(Scheduler scheduler,
@ -38,12 +43,14 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
ActionRepository repository,
ResourceService resourceService,
PluginService pluginService,
PageService pageService) {
PageService pageService,
PluginManager pluginManager) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.repository = repository;
this.resourceService = resourceService;
this.pluginService = pluginService;
this.pageService = pageService;
this.pluginManager = pluginManager;
}
@Override
@ -92,4 +99,39 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
.then(Mono.just(action1));
});
}
@Override
public Flux<Object> executeAction(ExecuteActionDTO executeActionDTO) {
String actionId = executeActionDTO.getActionId();
log.debug("Going to execute action with id: {}", actionId);
// 1. Fetch the query from the DB to get the type
Mono<Action> actionMono = repository.findById(actionId)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "action", actionId)));
// 2. Instantiate the implementation class based on the query type
Mono<Plugin> pluginMono = actionMono.flatMap(action -> pluginService.findById(action.getPluginId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "plugin")));
Mono<Resource> resourceMono = actionMono.flatMap(action -> resourceService.findById(action.getResourceId()))
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "resource")));
Mono<PluginExecutor> pluginExecutorMono = pluginMono.flatMap(plugin -> {
List<PluginExecutor> executorList = pluginManager.getExtensions(PluginExecutor.class, plugin.getExecutorClass());
if (executorList.isEmpty()) {
return Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "plugin", plugin.getExecutorClass()));
}
return Mono.just(executorList.get(0));
}
);
// 3. Execute the query
return actionMono.flatMap(action -> resourceMono.zipWith(pluginExecutorMono, (resource, pluginExecutor) ->
{
log.debug("*** About to invoke the plugin**");
// TODO: The CommandParams is being passed as null here. Move it to interfaces.CommandParams
return pluginExecutor.execute(resource.getResourceConfiguration(), action.getActionConfiguration(), executeActionDTO.getParams());
}))
.flatMapIterable(Flux::toIterable);
}
}

View File

@ -2,7 +2,7 @@ package com.appsmith.server.services;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.dtos.Param;
import com.appsmith.server.dtos.OldParam;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
@ -52,21 +52,21 @@ public abstract class OldPluginExecutor {
Map<String, String> queryMap = new HashMap<>();
Map<String, String> headerMap = new HashMap<>();
if (params.getQueryParams() != null) {
if (params.getQueryOldParams() != null) {
queryMap = params
.getQueryParams()
.getQueryOldParams()
.stream()
.collect(
Collectors.toMap(Param::getKey, Param::getValue,
Collectors.toMap(OldParam::getKey, OldParam::getValue,
// Incase there's a conflict, we pick the older value
(oldValue, newValue) -> oldValue));
}
if (params.getHeaderParams() != null) {
if (params.getHeaderOldParams() != null) {
headerMap = params
.getHeaderParams()
.getHeaderOldParams()
.stream()
.collect(
Collectors.toMap(Param::getKey, Param::getValue,
Collectors.toMap(OldParam::getKey, OldParam::getValue,
// Incase there's a conflict, we pick the older value
(oldValue, newValue) -> oldValue));
}

View File

@ -18,12 +18,11 @@ public interface PluginService extends CrudService<Plugin, String> {
*/
OldPluginExecutor getPluginExecutor(PluginType pluginType, String className);
public Mono<Organization> installPlugin(PluginOrgDTO plugin);
Mono<Organization> installPlugin(PluginOrgDTO plugin);
public Mono<Organization> uninstallPlugin(PluginOrgDTO plugin);
Mono<Organization> uninstallPlugin(PluginOrgDTO plugin);
public Mono<Plugin> findByName(String name);
public Mono<Plugin> findById(String id);
Mono<Plugin> findByName(String name);
Mono<Plugin> findById(String id);
}

View File

@ -1,10 +0,0 @@
package com.appsmith.server.services;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import reactor.core.publisher.Flux;
public interface QueryService extends CrudService<Query, String> {
Flux<Object> executeQuery(String name, CommandQueryParams params);
}

View File

@ -1,70 +0,0 @@
package com.appsmith.server.services;
import com.appsmith.external.plugins.PluginExecutor;
import com.appsmith.server.domains.Query;
import com.appsmith.server.dtos.CommandQueryParams;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.repositories.QueryRepository;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import javax.validation.Validator;
import java.util.List;
@Slf4j
@Service
public class QueryServiceImpl extends BaseService<QueryRepository, Query, String> implements QueryService {
PluginService pluginService;
PluginManager pluginManager;
@Autowired
public QueryServiceImpl(Scheduler scheduler,
Validator validator,
MongoConverter mongoConverter,
ReactiveMongoTemplate reactiveMongoTemplate,
QueryRepository repository,
PluginService pluginService,
PluginManager pluginManager) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
this.pluginService = pluginService;
this.pluginManager = pluginManager;
}
@Override
public Flux<Object> executeQuery(String name, CommandQueryParams params) {
log.debug("Going to execute query with name: {}", name);
// 1. Fetch the query from the DB to get the type
Mono<Query> queryMono = repository.findByName(name)
.switchIfEmpty(Mono.defer(() -> Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "query", name))));
// 2. Instantiate the implementation class based on the query type
Mono<PluginExecutor> pluginExecutorMono = queryMono.flatMap(queryObj -> {
String pluginId = queryObj.getPlugin().getExecutorClass();
List<PluginExecutor> executorList = pluginManager.getExtensions(PluginExecutor.class, pluginId);
if (executorList.isEmpty()) {
return Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "plugin"));
}
return Mono.just(executorList.get(0));
}
);
// 3. Execute the query
return queryMono
.zipWith(pluginExecutorMono, (queryObj, pluginExecutor) ->
// TODO: The CommandParams is being passed as null here. Move it to interfaces.CommandParams
pluginExecutor.execute(queryObj.getCommandTemplate(), null))
.flatMapIterable(Flux::toIterable);
}
}