Add support for reactive streams with mongodb. (#1720)
Add support for reactive streams with mongodb. (fixes 1480) 1. Replace mongodb driver with reactive mongodb driver. Change APIs accordingly. 2. Use webflux + reactor framework to execute mongodb queries in event loop model. 3. Add test to test MongoPluginExecutor class' method "testDatasource".
This commit is contained in:
parent
fc3197b78f
commit
2c2aa06e32
|
|
@ -44,11 +44,6 @@
|
|||
<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>
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -14,12 +14,16 @@ import com.appsmith.external.pluginExceptions.AppsmithPluginException;
|
|||
import com.appsmith.external.pluginExceptions.StaleConnectionException;
|
||||
import com.appsmith.external.plugins.BasePlugin;
|
||||
import com.appsmith.external.plugins.PluginExecutor;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.MongoCommandException;
|
||||
import com.mongodb.MongoTimeoutException;
|
||||
import com.mongodb.client.ClientSession;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.MongoClientURI;
|
||||
import com.mongodb.reactivestreams.client.MongoClient;
|
||||
import com.mongodb.reactivestreams.client.MongoClients;
|
||||
import com.mongodb.reactivestreams.client.ClientSession;
|
||||
import com.mongodb.reactivestreams.client.MongoCollection;
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase;
|
||||
import com.mongodb.reactivestreams.client.Success;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
|
|
@ -32,6 +36,7 @@ import org.pf4j.PluginWrapper;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
@ -81,7 +86,7 @@ public class MongoPlugin extends BasePlugin {
|
|||
* https://docs.huihoo.com/mongodb/3.4/reference/command/index.html
|
||||
*
|
||||
* @param mongoClient : This is the connection that is established to the data source. This connection is according
|
||||
* to the parameters in Datasource Configuration
|
||||
* to the parameters in Datasource Configuration
|
||||
* @param datasourceConfiguration : These are the configurations which have been used to create a Datasource from a Plugin
|
||||
* @param actionConfiguration : These are the configurations which have been used to create an Action from a Datasource.
|
||||
* @return Result data from executing the action's query.
|
||||
|
|
@ -96,71 +101,79 @@ public class MongoPlugin extends BasePlugin {
|
|||
throw new StaleConnectionException();
|
||||
}
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(getDatabaseName(datasourceConfiguration));
|
||||
Bson command = Document.parse(actionConfiguration.getBody());
|
||||
Mono<Document> mongoOutputMono = Mono.from(database.runCommand(command));
|
||||
ActionExecutionResult result = new ActionExecutionResult();
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(getDatabaseName(datasourceConfiguration));
|
||||
return mongoOutputMono
|
||||
.flatMap(mongoOutput -> {
|
||||
try {
|
||||
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();
|
||||
|
||||
Bson command = Document.parse(actionConfiguration.getBody());
|
||||
if (BigInteger.ONE.equals(status)) {
|
||||
result.setIsExecutionSuccess(true);
|
||||
|
||||
try {
|
||||
Document mongoOutput = database.runCommand(command);
|
||||
/**
|
||||
* For the `findAndModify` command, we don't get the count of modifications made. Instead,
|
||||
* we either get the modified new value or the pre-modified old value (depending on the
|
||||
* `new` field in the command. Let's return that value to the user.
|
||||
*/
|
||||
if (outputJson.has(VALUE_STR)) {
|
||||
result.setBody(objectMapper.readTree(
|
||||
cleanUp(new JSONObject().put(VALUE_STR, outputJson.get(VALUE_STR))).toString()
|
||||
));
|
||||
}
|
||||
|
||||
JSONObject outputJson = new JSONObject(mongoOutput.toJson());
|
||||
/**
|
||||
* 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 = (JSONArray) cleanUp(
|
||||
outputJson.getJSONObject("cursor").getJSONArray("firstBatch"));
|
||||
result.setBody(objectMapper.readTree(outputResult.toString()));
|
||||
}
|
||||
|
||||
//The output json contains the key "ok". This is the status of the command
|
||||
BigInteger status = outputJson.getBigInteger("ok");
|
||||
JSONArray headerArray = new JSONArray();
|
||||
/**
|
||||
* 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"));
|
||||
result.setBody(body);
|
||||
headerArray.put(body);
|
||||
}
|
||||
|
||||
if (BigInteger.ONE.equals(status)) {
|
||||
result.setIsExecutionSuccess(true);
|
||||
/**
|
||||
* The json key contains key "nModified" in case of update command. This signifies the no of
|
||||
* documents updated.
|
||||
*/
|
||||
if (outputJson.has(N_MODIFIED)) {
|
||||
JSONObject body = new JSONObject().put(N_MODIFIED, outputJson.getBigInteger(N_MODIFIED));
|
||||
result.setBody(body);
|
||||
headerArray.put(body);
|
||||
}
|
||||
|
||||
// For the `findAndModify` command, we don't get the count of modifications made. Instead, we either
|
||||
// get the modified new value or the pre-modified old value (depending on the `new` field in the
|
||||
// command. Let's return that value to the user.
|
||||
if (outputJson.has(VALUE_STR)) {
|
||||
result.setBody(objectMapper.readTree(
|
||||
cleanUp(new JSONObject().put(VALUE_STR, outputJson.get(VALUE_STR))).toString()
|
||||
));
|
||||
}
|
||||
/** 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.
|
||||
*/
|
||||
}
|
||||
|
||||
//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 = (JSONArray) cleanUp(
|
||||
outputJson.getJSONObject("cursor").getJSONArray("firstBatch"));
|
||||
result.setBody(objectMapper.readTree(outputResult.toString()));
|
||||
}
|
||||
JSONObject statusJson = new JSONObject().put("ok", status);
|
||||
headerArray.put(statusJson);
|
||||
result.setHeaders(objectMapper.readTree(headerArray.toString()));
|
||||
} catch (Exception e) {
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
|
||||
}
|
||||
|
||||
//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"));
|
||||
result.setBody(body);
|
||||
headerArray.put(body);
|
||||
}
|
||||
|
||||
//The json key contains key "nModified" in case of update command. This signifies the no of
|
||||
//documents updated.
|
||||
if (outputJson.has(N_MODIFIED)) {
|
||||
JSONObject body = new JSONObject().put(N_MODIFIED, outputJson.getBigInteger(N_MODIFIED));
|
||||
result.setBody(body);
|
||||
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(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
|
||||
}
|
||||
|
||||
return Mono.just(result);
|
||||
return Mono.just(result);
|
||||
});
|
||||
}
|
||||
|
||||
private String getDatabaseName(DatasourceConfiguration datasourceConfiguration) {
|
||||
|
|
@ -178,18 +191,20 @@ public class MongoPlugin extends BasePlugin {
|
|||
|
||||
@Override
|
||||
public Mono<MongoClient> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
|
||||
// TODO: ReadOnly seems to be not supported at the driver level. The recommendation is to connect with a
|
||||
// user that doesn't have write permissions on the database.
|
||||
// Ref: https://api.mongodb.com/java/2.13/com/mongodb/DB.html#setReadOnly-java.lang.Boolean-
|
||||
/**
|
||||
* TODO: ReadOnly seems to be not supported at the driver level. The recommendation is to connect with a
|
||||
* user that doesn't have write permissions on the database.
|
||||
* Ref: https://api.mongodb.com/java/2.13/com/mongodb/DB.html#setReadOnly-java.lang.Boolean-
|
||||
*/
|
||||
|
||||
try {
|
||||
return Mono.just(new MongoClient(buildClientURI(datasourceConfiguration)));
|
||||
return Mono.just(MongoClients.create(buildClientURI(datasourceConfiguration)));
|
||||
} catch (Exception e) {
|
||||
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
|
||||
}
|
||||
}
|
||||
|
||||
public static MongoClientURI buildClientURI(DatasourceConfiguration datasourceConfiguration) {
|
||||
public static String buildClientURI(DatasourceConfiguration datasourceConfiguration) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
final Connection connection = datasourceConfiguration.getConnection();
|
||||
|
|
@ -252,9 +267,7 @@ public class MongoPlugin extends BasePlugin {
|
|||
builder.deleteCharAt(builder.length() - 1);
|
||||
}
|
||||
|
||||
final String uri = builder.toString();
|
||||
log.info("MongoPlugin URI: `{}`.", uri);
|
||||
return new MongoClientURI(uri);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -313,56 +326,22 @@ public class MongoPlugin extends BasePlugin {
|
|||
|
||||
@Override
|
||||
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
|
||||
final Connection.Type connectionType = datasourceConfiguration.getConnection().getType();
|
||||
return datasourceCreate(datasourceConfiguration)
|
||||
.map(mongoClient -> {
|
||||
ClientSession clientSession = null;
|
||||
|
||||
try {
|
||||
// Not using try-with-resources here since we want to close the *session* before closing the
|
||||
// MongoClient instance.
|
||||
if (Connection.Type.REPLICA_SET.equals(connectionType)) {
|
||||
// For REPLICA_SET connections, we check by creating a session, as this is faster.
|
||||
clientSession = mongoClient.startSession();
|
||||
|
||||
} else {
|
||||
// For DIRECT connections, we check by running a DB command, as it's the only reliable
|
||||
// method of checking if the connection is usable.
|
||||
mongoClient
|
||||
.getDatabase("admin")
|
||||
.runCommand(new Document("listDatabases", 1));
|
||||
return new DatasourceTestResult();
|
||||
|
||||
}
|
||||
|
||||
} catch (MongoTimeoutException e) {
|
||||
log.warn("Timeout connecting to MongoDB from MongoPlugin.", e);
|
||||
return new DatasourceTestResult("Timed out trying to connect to MongoDB host.");
|
||||
|
||||
} catch (MongoCommandException e) {
|
||||
// The fact that we got a response saying "Unauthorized" means that the connection to the
|
||||
// MongoDB instance is valid. It also means we don't have access to the admin database, but
|
||||
// that's okay for our purposes here.
|
||||
return "Unauthorized".equals(e.getErrorCodeName())
|
||||
? new DatasourceTestResult()
|
||||
: new DatasourceTestResult(e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
return new DatasourceTestResult(e.getMessage());
|
||||
|
||||
} finally {
|
||||
if (clientSession != null) {
|
||||
clientSession.close();
|
||||
}
|
||||
if (mongoClient != null) {
|
||||
mongoClient.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new DatasourceTestResult();
|
||||
.flatMap(mongoClient -> {
|
||||
return Mono.zip(Mono.just(mongoClient),
|
||||
Mono.from(mongoClient.getDatabase("admin").runCommand(new Document(
|
||||
"listDatabases", 1))));
|
||||
})
|
||||
.onErrorResume(error -> Mono.just(new DatasourceTestResult(error.getMessage())));
|
||||
.doOnSuccess(tuple -> {
|
||||
MongoClient mongoClient = tuple.getT1();
|
||||
|
||||
if(mongoClient != null) {
|
||||
mongoClient.close();
|
||||
}
|
||||
})
|
||||
.then(Mono.just(new DatasourceTestResult()))
|
||||
.onErrorResume(error -> {
|
||||
return Mono.just(new DatasourceTestResult(error.getMessage()));});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -370,166 +349,181 @@ public class MongoPlugin extends BasePlugin {
|
|||
final DatasourceStructure structure = new DatasourceStructure();
|
||||
List<DatasourceStructure.Table> tables = new ArrayList<>();
|
||||
structure.setTables(tables);
|
||||
|
||||
final MongoDatabase database = mongoClient.getDatabase(getDatabaseName(datasourceConfiguration));
|
||||
|
||||
for (Document collection : database.listCollections()) {
|
||||
final String collectionName = collection.getString("name");
|
||||
return Flux.from(database.listCollectionNames()).
|
||||
flatMap(collectionName -> {
|
||||
final ArrayList<DatasourceStructure.Column> columns = new ArrayList<>();
|
||||
final ArrayList<DatasourceStructure.Template> templates = new ArrayList<>();
|
||||
tables.add(new DatasourceStructure.Table(
|
||||
DatasourceStructure.TableType.COLLECTION,
|
||||
collectionName,
|
||||
columns,
|
||||
new ArrayList<>(),
|
||||
templates
|
||||
));
|
||||
|
||||
final ArrayList<DatasourceStructure.Column> columns = new ArrayList<>();
|
||||
final ArrayList<DatasourceStructure.Template> templates = new ArrayList<>();
|
||||
tables.add(new DatasourceStructure.Table(
|
||||
DatasourceStructure.TableType.COLLECTION,
|
||||
collectionName,
|
||||
columns,
|
||||
new ArrayList<>(),
|
||||
templates
|
||||
));
|
||||
return Mono.zip(
|
||||
Mono.just(columns),
|
||||
Mono.just(templates),
|
||||
Mono.just(collectionName),
|
||||
Mono.from(database.getCollection(collectionName).find().limit(1).first())
|
||||
);
|
||||
}).
|
||||
flatMap(tuple -> {
|
||||
final ArrayList<DatasourceStructure.Column> columns = tuple.getT1();
|
||||
final ArrayList<DatasourceStructure.Template> templates = tuple.getT2();
|
||||
String collectionName = tuple.getT3();
|
||||
Document document = tuple.getT4();
|
||||
|
||||
final Document first = database.getCollection(collectionName).find().limit(1).first();
|
||||
if (first == null) {
|
||||
continue;
|
||||
}
|
||||
generateTemplatesAndStructureForACollection(collectionName, document, columns, templates);
|
||||
|
||||
String filterFieldName = null;
|
||||
String filterFieldValue = null;
|
||||
Map<String, String> sampleInsertValues = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : first.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
String type;
|
||||
|
||||
if (value instanceof Integer) {
|
||||
type = "Integer";
|
||||
sampleInsertValues.put(name, "1");
|
||||
} else if (value instanceof Long) {
|
||||
type = "Long";
|
||||
sampleInsertValues.put(name, "NumberLong(\"1\")");
|
||||
} else if (value instanceof Double) {
|
||||
type = "Double";
|
||||
sampleInsertValues.put(name, "1");
|
||||
} else if (value instanceof Decimal128) {
|
||||
type = "BigDecimal";
|
||||
sampleInsertValues.put(name, "NumberDecimal(\"1\")");
|
||||
} else if (value instanceof String) {
|
||||
type = "String";
|
||||
sampleInsertValues.put(name, "\"new value\"");
|
||||
if (filterFieldName == null || filterFieldName.compareTo(name) > 0) {
|
||||
filterFieldName = name;
|
||||
filterFieldValue = (String) value;
|
||||
}
|
||||
} else if (value instanceof ObjectId) {
|
||||
type = "ObjectId";
|
||||
if (!value.equals("_id")) {
|
||||
sampleInsertValues.put(name, "ObjectId(\"a_valid_object_id_hex\")");
|
||||
}
|
||||
} else if (value instanceof Collection) {
|
||||
type = "Array";
|
||||
sampleInsertValues.put(name, "[1, 2, 3]");
|
||||
} else if (value instanceof Date) {
|
||||
type = "Date";
|
||||
sampleInsertValues.put(name, "new Date(\"2019-07-01\")");
|
||||
} else {
|
||||
type = "Object";
|
||||
sampleInsertValues.put(name, "{}");
|
||||
}
|
||||
|
||||
columns.add(new DatasourceStructure.Column(name, type, null));
|
||||
}
|
||||
|
||||
columns.sort(Comparator.naturalOrder());
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Find",
|
||||
"{\n" +
|
||||
" \"find\": \"" + collectionName + "\",\n" +
|
||||
(
|
||||
filterFieldName == null ? "" :
|
||||
" \"filter\": {\n" +
|
||||
" \"" + filterFieldName + "\": \"" + filterFieldValue + "\"\n" +
|
||||
" },\n"
|
||||
) +
|
||||
" \"sort\": {\n" +
|
||||
" \"_id\": 1\n" +
|
||||
" },\n" +
|
||||
" \"limit\": 10\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Find by ID",
|
||||
"{\n" +
|
||||
" \"find\": \"" + collectionName + "\",\n" +
|
||||
" \"filter\": {\n" +
|
||||
" \"_id\": ObjectId(\"id_to_query_with\")\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
sampleInsertValues.entrySet().stream()
|
||||
.map(entry -> " \"" + entry.getKey() + "\": " + entry.getValue() + ",\n")
|
||||
.collect(Collectors.joining(""));
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Insert",
|
||||
"{\n" +
|
||||
" \"insert\": \"" + collectionName + "\",\n" +
|
||||
" \"documents\": [\n" +
|
||||
" {\n" +
|
||||
sampleInsertValues.entrySet().stream()
|
||||
.map(entry -> " \"" + entry.getKey() + "\": " + entry.getValue() + ",\n")
|
||||
.sorted()
|
||||
.collect(Collectors.joining("")) +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Update",
|
||||
"{\n" +
|
||||
" \"update\": \"" + collectionName + "\",\n" +
|
||||
" \"updates\": [\n" +
|
||||
" {\n" +
|
||||
" \"q\": {\n" +
|
||||
" \"_id\": ObjectId(\"id_of_document_to_update\")\n" +
|
||||
" },\n" +
|
||||
" \"u\": { \"$set\": { \"" + filterFieldName + "\": \"new value\" } }\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Delete",
|
||||
"{\n" +
|
||||
" \"delete\": \"" + collectionName + "\",\n" +
|
||||
" \"deletes\": [\n" +
|
||||
" {\n" +
|
||||
" \"q\": {\n" +
|
||||
" \"_id\": \"id_of_document_to_delete\"\n" +
|
||||
" },\n" +
|
||||
" \"limit\": 1\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
tables.sort(Comparator.comparing(DatasourceStructure.Table::getName));
|
||||
return Mono.just(structure);
|
||||
return Mono.just(structure);
|
||||
}).
|
||||
collectList().
|
||||
flatMap(documentList -> {
|
||||
return Mono.just(structure);
|
||||
});
|
||||
}
|
||||
|
||||
private static void generateTemplatesAndStructureForACollection(String collectionName,
|
||||
Document document,
|
||||
ArrayList<DatasourceStructure.Column> columns,
|
||||
ArrayList<DatasourceStructure.Template> templates) {
|
||||
String filterFieldName = null;
|
||||
String filterFieldValue = null;
|
||||
Map<String, String> sampleInsertValues = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : document.entrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final Object value = entry.getValue();
|
||||
String type;
|
||||
|
||||
if (value instanceof Integer) {
|
||||
type = "Integer";
|
||||
sampleInsertValues.put(name, "1");
|
||||
} else if (value instanceof Long) {
|
||||
type = "Long";
|
||||
sampleInsertValues.put(name, "NumberLong(\"1\")");
|
||||
} else if (value instanceof Double) {
|
||||
type = "Double";
|
||||
sampleInsertValues.put(name, "1");
|
||||
} else if (value instanceof Decimal128) {
|
||||
type = "BigDecimal";
|
||||
sampleInsertValues.put(name, "NumberDecimal(\"1\")");
|
||||
} else if (value instanceof String) {
|
||||
type = "String";
|
||||
sampleInsertValues.put(name, "\"new value\"");
|
||||
if (filterFieldName == null || filterFieldName.compareTo(name) > 0) {
|
||||
filterFieldName = name;
|
||||
filterFieldValue = (String) value;
|
||||
}
|
||||
} else if (value instanceof ObjectId) {
|
||||
type = "ObjectId";
|
||||
if (!value.equals("_id")) {
|
||||
sampleInsertValues.put(name, "ObjectId(\"a_valid_object_id_hex\")");
|
||||
}
|
||||
} else if (value instanceof Collection) {
|
||||
type = "Array";
|
||||
sampleInsertValues.put(name, "[1, 2, 3]");
|
||||
} else if (value instanceof Date) {
|
||||
type = "Date";
|
||||
sampleInsertValues.put(name, "new Date(\"2019-07-01\")");
|
||||
} else {
|
||||
type = "Object";
|
||||
sampleInsertValues.put(name, "{}");
|
||||
}
|
||||
|
||||
columns.add(new DatasourceStructure.Column(name, type, null));
|
||||
}
|
||||
|
||||
columns.sort(Comparator.naturalOrder());
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Find",
|
||||
"{\n" +
|
||||
" \"find\": \"" + collectionName + "\",\n" +
|
||||
(
|
||||
filterFieldName == null ? "" :
|
||||
" \"filter\": {\n" +
|
||||
" \"" + filterFieldName + "\": \"" + filterFieldValue + "\"\n" +
|
||||
" },\n"
|
||||
) +
|
||||
" \"sort\": {\n" +
|
||||
" \"_id\": 1\n" +
|
||||
" },\n" +
|
||||
" \"limit\": 10\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Find by ID",
|
||||
"{\n" +
|
||||
" \"find\": \"" + collectionName + "\",\n" +
|
||||
" \"filter\": {\n" +
|
||||
" \"_id\": ObjectId(\"id_to_query_with\")\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
sampleInsertValues.entrySet().stream()
|
||||
.map(entry -> " \"" + entry.getKey() + "\": " + entry.getValue() + ",\n")
|
||||
.collect(Collectors.joining(""));
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Insert",
|
||||
"{\n" +
|
||||
" \"insert\": \"" + collectionName + "\",\n" +
|
||||
" \"documents\": [\n" +
|
||||
" {\n" +
|
||||
sampleInsertValues.entrySet().stream()
|
||||
.map(entry -> " \"" + entry.getKey() + "\": " + entry.getValue() + ",\n")
|
||||
.sorted()
|
||||
.collect(Collectors.joining("")) +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Update",
|
||||
"{\n" +
|
||||
" \"update\": \"" + collectionName + "\",\n" +
|
||||
" \"updates\": [\n" +
|
||||
" {\n" +
|
||||
" \"q\": {\n" +
|
||||
" \"_id\": ObjectId(\"id_of_document_to_update\")\n" +
|
||||
" },\n" +
|
||||
" \"u\": { \"$set\": { \"" + filterFieldName + "\": \"new value\" } }\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
|
||||
templates.add(
|
||||
new DatasourceStructure.Template(
|
||||
"Delete",
|
||||
"{\n" +
|
||||
" \"delete\": \"" + collectionName + "\",\n" +
|
||||
" \"deletes\": [\n" +
|
||||
" {\n" +
|
||||
" \"q\": {\n" +
|
||||
" \"_id\": \"id_of_document_to_delete\"\n" +
|
||||
" },\n" +
|
||||
" \"limit\": 1\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}\n"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static String urlEncode(String text) {
|
||||
|
|
|
|||
|
|
@ -9,14 +9,24 @@ import com.appsmith.external.models.Endpoint;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
|
||||
import com.mongodb.reactivestreams.client.MongoClient;
|
||||
import com.mongodb.reactivestreams.client.MongoClients;
|
||||
import com.mongodb.reactivestreams.client.ClientSession;
|
||||
import com.mongodb.reactivestreams.client.MongoCollection;
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase;
|
||||
import com.mongodb.reactivestreams.client.Success;
|
||||
import org.reactivestreams.Publisher;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.bson.Document;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -25,14 +35,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for MongoPlugin
|
||||
*/
|
||||
|
||||
public class MongoPluginTest {
|
||||
|
||||
MongoPlugin.MongoPluginExecutor pluginExecutor = new MongoPlugin.MongoPluginExecutor();
|
||||
|
|
@ -51,23 +59,30 @@ public class MongoPluginTest {
|
|||
public static void setUp() {
|
||||
address = mongoContainer.getContainerIpAddress();
|
||||
port = mongoContainer.getFirstMappedPort();
|
||||
String uri = "mongodb://" + address + ":" + Integer.toString(port);
|
||||
final MongoClient mongoClient = MongoClients.create(uri);
|
||||
|
||||
final MongoClient mongoClient = new MongoClient(address, port);
|
||||
if (!mongoClient.getDatabase("test").listCollectionNames().iterator().hasNext()) {
|
||||
final MongoCollection<Document> usersCollection = mongoClient.getDatabase("test").getCollection("users");
|
||||
usersCollection.insertMany(List.of(
|
||||
new Document(Map.of(
|
||||
"name", "Cierra Vega",
|
||||
"gender", "F",
|
||||
"age", 20,
|
||||
"luckyNumber", 987654321L,
|
||||
"dob", LocalDate.of(2018, 12, 31),
|
||||
"netWorth", new BigDecimal("123456.789012")
|
||||
)),
|
||||
new Document(Map.of("name", "Alden Cantrell", "gender", "M", "age", 30)),
|
||||
new Document(Map.of("name", "Kierra Gentry", "gender", "F", "age", 40))
|
||||
));
|
||||
}
|
||||
Flux.from(mongoClient.getDatabase("test").listCollectionNames()).collectList().
|
||||
flatMap(collectionNamesList -> {
|
||||
final MongoCollection<Document> usersCollection = mongoClient.getDatabase("test").getCollection(
|
||||
"users");
|
||||
if(collectionNamesList.size() == 0) {
|
||||
Mono.from(usersCollection.insertMany(List.of(
|
||||
new Document(Map.of(
|
||||
"name", "Cierra Vega",
|
||||
"gender", "F",
|
||||
"age", 20,
|
||||
"luckyNumber", 987654321L,
|
||||
"dob", LocalDate.of(2018, 12, 31),
|
||||
"netWorth", new BigDecimal("123456.789012")
|
||||
)),
|
||||
new Document(Map.of("name", "Alden Cantrell", "gender", "M", "age", 30)),
|
||||
new Document(Map.of("name", "Kierra Gentry", "gender", "F", "age", 40))
|
||||
))).block();
|
||||
}
|
||||
|
||||
return Mono.just(usersCollection);
|
||||
}).block();
|
||||
}
|
||||
|
||||
private DatasourceConfiguration createDatasourceConfiguration() {
|
||||
|
|
@ -103,6 +118,25 @@ public class MongoPluginTest {
|
|||
.verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Test "testDatasource" method in MongoPluginExecutor class.
|
||||
*/
|
||||
@Test
|
||||
public void testDatasourceFail() {
|
||||
System.out.println(mongoContainer.getContainerIpAddress());
|
||||
System.out.println(mongoContainer.getFirstMappedPort());
|
||||
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
|
||||
dsConfig.getEndpoints().get(0).setHost("badHost");
|
||||
System.out.println(dsConfig);
|
||||
|
||||
StepVerifier.create(pluginExecutor.testDatasource(dsConfig))
|
||||
.assertNext(datasourceTestResult -> {
|
||||
assertNotNull(datasourceTestResult);
|
||||
assertFalse(datasourceTestResult.isSuccess());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteReadQuery() {
|
||||
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
|
||||
|
|
@ -311,5 +345,4 @@ public class MongoPluginTest {
|
|||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user