fix: add support for reading DBRef type in Mongo plugin (#12670)

* DBRef fix with codec

* Added code comments regarding custom codec implemented

* Added Unit Test for Mongo DBRef

Co-authored-by: Leo Thomas <leoweb2010@gmail.com>
Co-authored-by: Sumit Kumar <sumit@appsmith.com>
This commit is contained in:
Leo Thomas 2022-04-11 09:38:17 +05:30 committed by GitHub
parent db2310bef7
commit f8cc2f5e54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 4 deletions

View File

@ -30,14 +30,25 @@ import com.external.plugins.utils.MongoErrorUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mongodb.DBObjectCodecProvider;
import com.mongodb.DBRefCodecProvider;
import com.mongodb.MongoCommandException;
import com.mongodb.MongoSocketWriteException;
import com.mongodb.MongoTimeoutException;
import com.mongodb.client.gridfs.codecs.GridFSFileCodecProvider;
import com.mongodb.client.model.geojson.codecs.GeoJsonCodecProvider;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import com.mongodb.reactivestreams.client.MongoDatabase;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.codecs.BsonTypeClassMap;
import org.bson.codecs.BsonValueCodecProvider;
import org.bson.codecs.DocumentCodec;
import org.bson.codecs.DocumentCodecProvider;
import org.bson.codecs.ValueCodecProvider;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.json.JSONArray;
import org.json.JSONObject;
@ -98,6 +109,7 @@ import static com.external.plugins.constants.FieldName.UPDATE_OPERATION;
import static com.external.plugins.constants.FieldName.UPDATE_QUERY;
import static com.external.plugins.utils.MongoPluginUtils.urlEncode;
import static java.lang.Boolean.TRUE;
import static java.util.Arrays.asList;
import static org.apache.logging.log4j.util.Strings.isBlank;
public class MongoPlugin extends BasePlugin {
@ -178,7 +190,7 @@ public class MongoPlugin extends BasePlugin {
private static final Integer MONGO_COMMAND_EXCEPTION_UNAUTHORIZED_ERROR_CODE = 13;
private static final Set<String> bsonFields = new HashSet<>(Arrays.asList(
private static final Set<String> bsonFields = new HashSet<>(asList(
AGGREGATE_PIPELINES,
COUNT_QUERY,
DELETE_QUERY,
@ -193,6 +205,18 @@ public class MongoPlugin extends BasePlugin {
private static final MongoErrorUtils mongoErrorUtils = MongoErrorUtils.getInstance();
private static final CodecRegistry DEFAULT_REGISTRY = CodecRegistries.fromProviders(
asList(new ValueCodecProvider(),
new BsonValueCodecProvider(),
new DocumentCodecProvider(),
new DBRefCodecProvider(),
new DBObjectCodecProvider(),
new BsonValueCodecProvider(),
new GeoJsonCodecProvider(),
new GridFSFileCodecProvider()));
private static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP = new org.bson.codecs.BsonTypeClassMap();
public MongoPlugin(PluginWrapper wrapper) {
super(wrapper);
}
@ -332,7 +356,17 @@ public class MongoPlugin extends BasePlugin {
)
.flatMap(mongoOutput -> {
try {
JSONObject outputJson = new JSONObject(mongoOutput.toJson());
/*
* Added Custom codec for JSON conversion since MongoDB Reactive API does not support
* processing of DbRef Object.
* https://github.com/spring-projects/spring-data-mongodb/issues/3015 : Mark Paluch commented
*/
DocumentCodec documentCodec = new DocumentCodec(
DEFAULT_REGISTRY,
DEFAULT_BSON_TYPE_CLASS_MAP
);
JSONObject outputJson = new JSONObject(mongoOutput.toJson(documentCodec));
//The output json contains the key "ok". This is the status of the command
BigInteger status = outputJson.getBigInteger("ok");

View File

@ -17,7 +17,9 @@ import com.appsmith.external.models.ParsedDataType;
import com.appsmith.external.models.Property;
import com.appsmith.external.models.RequestParamDTO;
import com.appsmith.external.models.SSLDetails;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mongodb.MongoCommandException;
@ -26,6 +28,7 @@ import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
import com.mongodb.DBRef;
import org.bson.Document;
import org.bson.types.BSONTimestamp;
import org.bson.types.Decimal128;
@ -135,8 +138,25 @@ public class MongoPluginTest {
)),
new Document(Map.of("name", "Kierra Gentry", "gender", "F", "age", 40))
))).block();
}
final MongoCollection<Document> addressCollection = mongoClient.getDatabase("test")
.getCollection("address");
Mono.from(addressCollection.insertMany(List.of(
new Document(Map.of(
"user", new DBRef("test", "users", "1"),
"street", "First Street",
"city", "Line One",
"state", "UP"
)),
new Document(Map.of(
"user", new DBRef("AAA", "BBB", "2000"),
"street", "Second Street",
"city", "Line Two",
"state", "UP"
))
))).block();
}
return Mono.empty();
}).block();
}
@ -249,6 +269,55 @@ public class MongoPluginTest {
.verifyComplete();
}
/**
* Test for DBRef after codec implementation
*/
@Test
public void testExecuteQueryDBRef() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
Mono<MongoClient> dsConnectionMono = pluginExecutor.datasourceCreate(dsConfig);
ActionConfiguration actionConfiguration = new ActionConfiguration();
Map<String, Object> configMap = new HashMap<>();
setValueSafelyInFormData(configMap, SMART_SUBSTITUTION, Boolean.TRUE);
setValueSafelyInFormData(configMap, COMMAND, "RAW");
setValueSafelyInFormData(configMap, BODY, "{\n" +
" find: \"address\",\n" +
" limit: 10,\n" +
" }");
actionConfiguration.setFormData(configMap);
Mono<Object> executeMono = dsConnectionMono.flatMap(conn -> pluginExecutor.executeParameterized(conn, new ExecuteActionDTO(), dsConfig, actionConfiguration));
StepVerifier.create(executeMono)
.assertNext(obj -> {
ActionExecutionResult result = (ActionExecutionResult) obj;
assertNotNull(result);
assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody());
assertEquals(2, ((ArrayNode) result.getBody()).size());
/*
* Provided Input : new DBRef("test", "users", "1")
* To test if we are getting the expected output after external codec implementation.
* Note: when the codec is removed from the MongoDBPlugin, this is found failing
*/
try {
ObjectMapper mapper = new ObjectMapper();
String expectedOutputJsonString = "{\"$db\":\"test\",\"$ref\":\"users\",\"$id\":\"1\"}";
JsonNode outputNode = mapper.readTree(expectedOutputJsonString);
assertEquals(outputNode, (((ArrayNode) result.getBody()).findValue("user")));
} catch (JsonProcessingException e) {
assert false;
}
})
.verifyComplete();
}
@Test
public void testExecuteReadQuery() {
DatasourceConfiguration dsConfig = createDatasourceConfiguration();
@ -475,8 +544,13 @@ public class MongoPluginTest {
StepVerifier.create(structureMono)
.assertNext(structure -> {
//Sort the Tables since one more table is added and to maintain sequence
structure.getTables().sort(
(DatasourceStructure.Table t1, DatasourceStructure.Table t2)
->t2.getName().compareTo(t1.getName())
);
assertNotNull(structure);
assertEquals(1, structure.getTables().size());
assertEquals(2, structure.getTables().size());
final DatasourceStructure.Table usersTable = structure.getTables().get(0);
assertEquals("users", usersTable.getName());