From a82a93109385bc2f722fcc71dd0ad082b79e0d11 Mon Sep 17 00:00:00 2001 From: Arpit Mohan Date: Sun, 31 Mar 2019 21:22:06 +0530 Subject: [PATCH] Adding mustache template substitution to QueryObj. Now we can query postgres DB on the fly! Yipee! --- app/server/pom.xml | 7 ++ .../server/controllers/QueryController.java | 5 +- .../server/services/PluginExecutor.java | 74 +++++++++++++++++- .../services/PostgresDBPluginExecutor.java | 76 ++++++++++++++++--- .../server/services/QueryService.java | 2 - .../server/services/QueryServiceImpl.java | 20 +++-- .../resources/application-local.properties | 8 ++ .../resources/application-staging.properties | 6 ++ 8 files changed, 174 insertions(+), 24 deletions(-) diff --git a/app/server/pom.xml b/app/server/pom.xml index 686310f92e..72e3b0c3c7 100644 --- a/app/server/pom.xml +++ b/app/server/pom.xml @@ -60,17 +60,24 @@ lombok true + + com.github.spullara.mustache.java + compiler + 0.9.6 + org.springframework.boot spring-boot-starter-test test + org.springframework.security spring-security-test test + diff --git a/app/server/src/main/java/com/mobtools/server/controllers/QueryController.java b/app/server/src/main/java/com/mobtools/server/controllers/QueryController.java index 8a768404ab..f4e7544832 100644 --- a/app/server/src/main/java/com/mobtools/server/controllers/QueryController.java +++ b/app/server/src/main/java/com/mobtools/server/controllers/QueryController.java @@ -3,10 +3,8 @@ package com.mobtools.server.controllers; import com.mobtools.server.constants.Url; import com.mobtools.server.domains.Query; import com.mobtools.server.dtos.CommandQueryParams; -import com.mobtools.server.dtos.ResponseDto; import com.mobtools.server.services.QueryService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; @@ -21,7 +19,6 @@ public class QueryController extends BaseController @PostMapping("/execute/{id}") public Flux executeQuery(@PathVariable String id, @RequestBody CommandQueryParams params) { - return service.executeQuery(id, params) - .map(result -> new ResponseDto<>(HttpStatus.OK.value(), result, null)); + return service.executeQuery(id, params); } } diff --git a/app/server/src/main/java/com/mobtools/server/services/PluginExecutor.java b/app/server/src/main/java/com/mobtools/server/services/PluginExecutor.java index 9068f09599..9ded2cc31e 100644 --- a/app/server/src/main/java/com/mobtools/server/services/PluginExecutor.java +++ b/app/server/src/main/java/com/mobtools/server/services/PluginExecutor.java @@ -1,8 +1,78 @@ package com.mobtools.server.services; +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import com.mobtools.server.domains.Query; +import com.mobtools.server.dtos.CommandQueryParams; +import com.mobtools.server.dtos.Param; import reactor.core.publisher.Flux; -public interface PluginExecutor { +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; - Flux execute(); +public abstract class PluginExecutor { + + /** + * This function executes the command against a backend datasource. + * All the variables in commandTemplate of the Query object has already been replaced with actual values + * by the #replaceTemplate() function + * + * @param query + * @param params + * @return Flux + */ + abstract Flux execute(Query query, CommandQueryParams params); + + /** + * This function should be run when the plugin is initialized + */ + abstract void init(); + + /** + * This function should be run when the plugin is destroyed + */ + abstract void destroy(); + + /** + * This function replaces the variables in the query commandTemplate with the actual params + * Executors can override this function to provide their own implementation if they wish to do something custom + * + * @param query Query + * @param params CommandQueryParams + */ + Query replaceTemplate(Query query, CommandQueryParams params) { + MustacheFactory mf = new DefaultMustacheFactory(); + Mustache mustache = mf.compile(new StringReader(query.getCommandTemplate()), "commandTemplate"); + Writer writer = new StringWriter(); + + Map queryMap = new HashMap<>(); + Map headerMap = new HashMap<>(); + if(params.getQueryParams() != null) { + queryMap = params + .getQueryParams() + .stream() + .collect( + Collectors.toMap(Param::getKey, Param::getValue, + // Incase there's a conflict, we pick the older value + (oldValue, newValue) -> oldValue)); + } + if(params.getHeaderParams() != null) { + headerMap = params + .getHeaderParams() + .stream() + .collect( + Collectors.toMap(Param::getKey, Param::getValue, + // Incase there's a conflict, we pick the older value + (oldValue, newValue) -> oldValue)); + } + mustache.execute(writer, queryMap); + + query.setCommandTemplate(writer.toString()); + return query; + } } diff --git a/app/server/src/main/java/com/mobtools/server/services/PostgresDBPluginExecutor.java b/app/server/src/main/java/com/mobtools/server/services/PostgresDBPluginExecutor.java index 40f7c1b4bc..9b6d68029f 100644 --- a/app/server/src/main/java/com/mobtools/server/services/PostgresDBPluginExecutor.java +++ b/app/server/src/main/java/com/mobtools/server/services/PostgresDBPluginExecutor.java @@ -1,25 +1,81 @@ package com.mobtools.server.services; -import com.mobtools.server.domains.Property; +import com.mobtools.server.domains.Query; +import com.mobtools.server.dtos.CommandQueryParams; +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.*; import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +@Slf4j @Component -public class PostgresDBPluginExecutor implements PluginExecutor { +public class PostgresDBPluginExecutor extends PluginExecutor { + + // JDBC driver name and database URL + @Value("${jdbc.postgres.driver}") + static final String JDBC_DRIVER = "org.postgresql.Driver"; + + @Value("${jdbc.postgres.url}") + static final String DB_URL = "jdbc:postgresql://localhost/mobtools"; + + // Database credentials + @Value("${jdbc.postgres.username}") + static final String DB_USER = "postgres"; + + @Value("${jdbc.postgres.password}") + static final String DB_PASS = "root"; + + Connection conn = null; @Override - public Flux execute() { - Property prop = new Property(); - prop.setKey("name"); - prop.setValue("val"); + public Flux execute(Query queryObj, CommandQueryParams params) { + if(conn == null) { + init(); + } + ArrayList list = new ArrayList(50); + try { + Statement statement = conn.createStatement(); + String queryTemplate = queryObj.getCommandTemplate(); - List props= new ArrayList<>(); - props.add(prop); + 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); + + // 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() { - return Flux.just(props); } } diff --git a/app/server/src/main/java/com/mobtools/server/services/QueryService.java b/app/server/src/main/java/com/mobtools/server/services/QueryService.java index ee13cce5a2..2442a8d6b2 100644 --- a/app/server/src/main/java/com/mobtools/server/services/QueryService.java +++ b/app/server/src/main/java/com/mobtools/server/services/QueryService.java @@ -4,8 +4,6 @@ import com.mobtools.server.domains.Query; import com.mobtools.server.dtos.CommandQueryParams; import reactor.core.publisher.Flux; -import java.util.stream.DoubleStream; - public interface QueryService extends CrudService { Flux executeQuery(String id, CommandQueryParams params); diff --git a/app/server/src/main/java/com/mobtools/server/services/QueryServiceImpl.java b/app/server/src/main/java/com/mobtools/server/services/QueryServiceImpl.java index 30f474e3ba..76eb5db1dd 100644 --- a/app/server/src/main/java/com/mobtools/server/services/QueryServiceImpl.java +++ b/app/server/src/main/java/com/mobtools/server/services/QueryServiceImpl.java @@ -35,12 +35,20 @@ public class QueryServiceImpl extends BaseService Mono.error(new MobtoolsException("Unable to find query by id: " + id)))) - // 2. Instantiate the implementation class based on the query type - .map(queryObj -> pluginService.getPluginExecutor(queryObj.getPlugin().getType(), queryObj.getPlugin().getExecutorClass())) - // 3. Execute the query - .flatMapMany(plugin -> plugin.execute()); + Mono queryMono = repository.findById(id) + .switchIfEmpty(Mono.defer(() -> Mono.error(new MobtoolsException("Unable to find query by id: " + id)))); + + // 2. Instantiate the implementation class based on the query type + Mono pluginExecutorMono = queryMono.map(queryObj -> + pluginService.getPluginExecutor(queryObj.getPlugin().getType(), queryObj.getPlugin().getExecutorClass())); + + // 3. Execute the query + return queryMono + .zipWith(pluginExecutorMono, (queryObj, pluginExecutor) -> { + Query newQueryObj = pluginExecutor.replaceTemplate(queryObj, params); + return pluginExecutor.execute(newQueryObj, params); + }) + .flatMapIterable(Flux::toIterable).subscribeOn(scheduler); } } diff --git a/app/server/src/main/resources/application-local.properties b/app/server/src/main/resources/application-local.properties index 11f3acab0c..cf17cdabec 100644 --- a/app/server/src/main/resources/application-local.properties +++ b/app/server/src/main/resources/application-local.properties @@ -4,3 +4,11 @@ spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 #spring.data.mongodb.username= #spring.data.mongodb.password= + +logging.level.com.mobtools=debug + +# JDBC Postgres properties +jdbc.postgres.driver=org.postgresql.Driver +jdbc.postgres.url=jdbc:postgresql://localhost/mobtools +jdbc.postgres.username=postgres +jdbc.postgres.password=root diff --git a/app/server/src/main/resources/application-staging.properties b/app/server/src/main/resources/application-staging.properties index 43e1077e64..594c4b2389 100644 --- a/app/server/src/main/resources/application-staging.properties +++ b/app/server/src/main/resources/application-staging.properties @@ -2,3 +2,9 @@ spring.data.mongodb.database=mobtools spring.data.mongodb.uri=mongodb+srv://admin:Y9PuxM52gcP3Dgfo@mobtools-test-cluster-swrsq.mongodb.net/mobtools?retryWrites=true spring.data.mongodb.authentication-database=admin + +# JDBC Postgres properties +jdbc.postgres.driver=org.postgresql.Driver +jdbc.postgres.url=jdbc:postgresql://ec2-54-247-85-251.eu-west-1.compute.amazonaws.com/d266aalso50024 +jdbc.postgres.username=pornlzmggggpgk +jdbc.postgres.password=09275163cd7e737baf4c210b5e8db8ed88ddb7a0ee9acc82416fd75346ea4bbb \ No newline at end of file