/*
* Copyright 2015 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.client;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.model.BulkWriteOptions;
import com.mongodb.client.model.Collation;
import com.mongodb.client.model.CollationAlternate;
import com.mongodb.client.model.CollationCaseFirst;
import com.mongodb.client.model.CollationMaxVariable;
import com.mongodb.client.model.CollationStrength;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.client.model.InsertOneModel;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateOneModel;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.WriteModel;
import com.mongodb.client.result.UpdateResult;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonNull;
import org.bson.BsonValue;
import org.junit.Assume;
import org.junit.AssumptionViolatedException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
public class JsonPoweredCrudTestHelper {
private final String description;
private final MongoCollection<BsonDocument> collection;
public JsonPoweredCrudTestHelper(final String description, final MongoCollection<BsonDocument> collection) {
this.description = description;
this.collection = collection;
}
BsonDocument getOperationResults(final BsonDocument operation) {
String name = operation.getString("name").getValue();
BsonDocument arguments = operation.getDocument("arguments");
String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1) + "Result";
try {
Method method = getClass().getDeclaredMethod(methodName, BsonDocument.class);
return (BsonDocument) method.invoke(this, arguments);
} catch (NoSuchMethodException e) {
throw new UnsupportedOperationException("No handler for operation " + methodName);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof AssumptionViolatedException) {
throw (AssumptionViolatedException) e.getTargetException();
}
if (e.getTargetException() instanceof MongoException) {
throw (MongoException) e.getTargetException();
}
throw (RuntimeException) e.getTargetException();
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException("Invalid handler access for operation " + methodName);
}
}
BsonDocument toResult(final int count) {
return toResult(new BsonInt32(count));
}
BsonDocument toResult(final MongoIterable<BsonDocument> results) {
return toResult(new BsonArray(results.into(new ArrayList<BsonDocument>())));
}
BsonDocument toResult(final String key, final BsonValue value) {
return toResult(new BsonDocument(key, value));
}
BsonDocument toResult(final UpdateResult updateResult) {
BsonDocument resultDoc = new BsonDocument("matchedCount", new BsonInt32((int) updateResult.getMatchedCount()));
if (updateResult.isModifiedCountAvailable()) {
resultDoc.append("modifiedCount", new BsonInt32((int) updateResult.getModifiedCount()));
}
// If the upsertedId is an ObjectId that means it came from the server and can't be verified.
// This check is to handle the "ReplaceOne with upsert when no documents match without an id specified" test
// in replaceOne-pre_2.6
if (updateResult.getUpsertedId() != null && !updateResult.getUpsertedId().isObjectId()) {
resultDoc.append("upsertedId", updateResult.getUpsertedId());
}
resultDoc.append("upsertedCount", updateResult.getUpsertedId() == null ? new BsonInt32(0) : new BsonInt32(1));
return toResult(resultDoc);
}
BsonDocument toResult(final BulkWriteResult bulkWriteResult) {
BsonDocument resultDoc = new BsonDocument(); // TODO: complete this, but not needed for command monitoring tests
return toResult(resultDoc);
}
BsonDocument toResult(final BsonValue results) {
return new BsonDocument("result", results != null ? results : BsonNull.VALUE);
}
BsonDocument getAggregateResult(final BsonDocument arguments) {
if (!serverVersionAtLeast(2, 6)) {
Assume.assumeFalse(description.contains("$out"));
}
List<BsonDocument> pipeline = new ArrayList<BsonDocument>();
for (BsonValue stage : arguments.getArray("pipeline")) {
pipeline.add(stage.asDocument());
}
AggregateIterable<BsonDocument> iterable = collection.aggregate(pipeline);
if (arguments.containsKey("batchSize")) {
iterable.batchSize(arguments.getNumber("batchSize").intValue());
}
if (arguments.containsKey("collation")) {
iterable.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(iterable);
}
BsonDocument getCountResult(final BsonDocument arguments) {
CountOptions options = new CountOptions();
if (arguments.containsKey("skip")) {
options.skip(arguments.getNumber("skip").intValue());
}
if (arguments.containsKey("limit")) {
options.limit(arguments.getNumber("limit").intValue());
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult((int) collection.count(arguments.getDocument("filter"), options));
}
BsonDocument getDistinctResult(final BsonDocument arguments) {
DistinctIterable<BsonValue> iterable = collection.distinct(arguments.getString("fieldName").getValue(), BsonValue.class);
if (arguments.containsKey("filter")) {
iterable.filter(arguments.getDocument("filter"));
}
if (arguments.containsKey("collation")) {
iterable.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(iterable.into(new BsonArray()));
}
@SuppressWarnings("deprecation")
BsonDocument getFindResult(final BsonDocument arguments) {
FindIterable<BsonDocument> iterable = collection.find(arguments.getDocument("filter"));
if (arguments.containsKey("skip")) {
iterable.skip(arguments.getNumber("skip").intValue());
}
if (arguments.containsKey("limit")) {
iterable.limit(arguments.getNumber("limit").intValue());
}
if (arguments.containsKey("batchSize")) {
iterable.batchSize(arguments.getNumber("batchSize").intValue());
}
if (arguments.containsKey("sort")) {
iterable.sort(arguments.getDocument("sort"));
}
if (arguments.containsKey("modifiers")) {
iterable.modifiers(arguments.getDocument("modifiers"));
}
if (arguments.containsKey("collation")) {
iterable.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(iterable);
}
BsonDocument getDeleteManyResult(final BsonDocument arguments) {
DeleteOptions options = new DeleteOptions();
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult("deletedCount",
new BsonInt32((int) collection.deleteMany(arguments.getDocument("filter"), options).getDeletedCount()));
}
BsonDocument getDeleteOneResult(final BsonDocument arguments) {
DeleteOptions options = new DeleteOptions();
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult("deletedCount",
new BsonInt32((int) collection.deleteOne(arguments.getDocument("filter"), options).getDeletedCount()));
}
BsonDocument getFindOneAndDeleteResult(final BsonDocument arguments) {
FindOneAndDeleteOptions options = new FindOneAndDeleteOptions();
if (arguments.containsKey("projection")) {
options.projection(arguments.getDocument("projection"));
}
if (arguments.containsKey("sort")) {
options.sort(arguments.getDocument("sort"));
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection.findOneAndDelete(arguments.getDocument("filter"), options));
}
BsonDocument getFindOneAndReplaceResult(final BsonDocument arguments) {
// in 2.4 the server can ignore the supplied _id and creates an ObjectID
Assume.assumeTrue(serverVersionAtLeast(2, 6));
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions();
if (arguments.containsKey("projection")) {
options.projection(arguments.getDocument("projection"));
}
if (arguments.containsKey("sort")) {
options.sort(arguments.getDocument("sort"));
}
if (arguments.containsKey("upsert")) {
options.upsert(arguments.getBoolean("upsert").getValue());
}
if (arguments.containsKey("returnDocument")) {
options.returnDocument(arguments.getString("returnDocument").getValue().equals("After") ? ReturnDocument.AFTER
: ReturnDocument.BEFORE);
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection
.findOneAndReplace(arguments.getDocument("filter"), arguments.getDocument("replacement"), options));
}
BsonDocument getFindOneAndUpdateResult(final BsonDocument arguments) {
FindOneAndUpdateOptions options = new FindOneAndUpdateOptions();
if (arguments.containsKey("projection")) {
options.projection(arguments.getDocument("projection"));
}
if (arguments.containsKey("sort")) {
options.sort(arguments.getDocument("sort"));
}
if (arguments.containsKey("upsert")) {
options.upsert(arguments.getBoolean("upsert").getValue());
}
if (arguments.containsKey("returnDocument")) {
options.returnDocument(arguments.getString("returnDocument").getValue().equals("After") ? ReturnDocument.AFTER
: ReturnDocument.BEFORE);
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection
.findOneAndUpdate(arguments.getDocument("filter"), arguments.getDocument("update"), options));
}
BsonDocument getInsertOneResult(final BsonDocument arguments) {
BsonDocument document = arguments.getDocument("document");
collection.insertOne(document);
return toResult(new BsonDocument("insertedId", document.get("_id")));
}
BsonDocument getInsertManyResult(final BsonDocument arguments) {
List<BsonDocument> documents = new ArrayList<BsonDocument>();
for (BsonValue document : arguments.getArray("documents")) {
documents.add(document.asDocument());
}
collection.insertMany(documents,
new InsertManyOptions().ordered(arguments.getBoolean("ordered", BsonBoolean.TRUE).getValue()));
BsonArray insertedIds = new BsonArray();
for (BsonDocument document : documents) {
insertedIds.add(document.get("_id"));
}
return toResult(new BsonDocument("insertedIds", insertedIds));
}
BsonDocument getReplaceOneResult(final BsonDocument arguments) {
UpdateOptions options = new UpdateOptions();
if (arguments.containsKey("upsert")) {
options.upsert(arguments.getBoolean("upsert").getValue());
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection
.replaceOne(arguments.getDocument("filter"), arguments.getDocument("replacement"), options));
}
BsonDocument getUpdateManyResult(final BsonDocument arguments) {
UpdateOptions options = new UpdateOptions();
if (arguments.containsKey("upsert")) {
options.upsert(arguments.getBoolean("upsert").getValue());
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection.updateMany(arguments.getDocument("filter"), arguments.getDocument("update"), options));
}
BsonDocument getUpdateOneResult(final BsonDocument arguments) {
UpdateOptions options = new UpdateOptions();
if (arguments.containsKey("upsert")) {
options.upsert(arguments.getBoolean("upsert").getValue());
}
if (arguments.containsKey("collation")) {
options.collation(getCollation(arguments.getDocument("collation")));
}
return toResult(collection.updateOne(arguments.getDocument("filter"), arguments.getDocument("update"), options));
}
BsonDocument getBulkWriteResult(final BsonDocument arguments) {
WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED;
if (arguments.containsKey("writeConcern")) {
if (arguments.getDocument("writeConcern").size() > 1) {
throw new UnsupportedOperationException("Write concern document contains unexpected keys: "
+ arguments.getDocument("writeConcern").keySet());
}
writeConcern = new WriteConcern(arguments.getDocument("writeConcern").getInt32("w").intValue());
}
List<WriteModel<BsonDocument>> writeModels = new ArrayList<WriteModel<BsonDocument>>();
for (Iterator<BsonValue> iter = arguments.getArray("requests").iterator(); iter.hasNext();) {
BsonDocument cur = iter.next().asDocument();
if (cur.get("insertOne") != null) {
writeModels.add(new InsertOneModel<BsonDocument>(cur.getDocument("insertOne").getDocument("document")));
} else if (cur.get("updateOne") != null) {
writeModels.add(new UpdateOneModel<BsonDocument>(cur.getDocument("updateOne").getDocument("filter"),
cur.getDocument("updateOne").getDocument("update")));
} else {
throw new UnsupportedOperationException("Unsupported write request type");
}
}
return toResult(collection.withWriteConcern(writeConcern).bulkWrite(writeModels,
new BulkWriteOptions()
.ordered(arguments.getBoolean("ordered", BsonBoolean.TRUE)
.getValue())));
}
Collation getCollation(final BsonDocument bsonCollation) {
Collation.Builder builder = Collation.builder();
if (bsonCollation.containsKey("locale")) {
builder.locale(bsonCollation.getString("locale").getValue());
}
if (bsonCollation.containsKey("caseLevel")) {
builder.caseLevel(bsonCollation.getBoolean("caseLevel").getValue());
}
if (bsonCollation.containsKey("caseFirst")) {
builder.collationCaseFirst(CollationCaseFirst.fromString(bsonCollation.getString("caseFirst").getValue()));
}
if (bsonCollation.containsKey("strength")) {
builder.collationStrength(CollationStrength.fromInt(bsonCollation.getInt32("strength").getValue()));
}
if (bsonCollation.containsKey("numericOrdering")) {
builder.numericOrdering(bsonCollation.getBoolean("numericOrdering").getValue());
}
if (bsonCollation.containsKey("strength")) {
builder.collationStrength(CollationStrength.fromInt(bsonCollation.getInt32("strength").getValue()));
}
if (bsonCollation.containsKey("alternate")) {
builder.collationAlternate(CollationAlternate.fromString(bsonCollation.getString("alternate").getValue()));
}
if (bsonCollation.containsKey("maxVariable")) {
builder.collationMaxVariable(CollationMaxVariable.fromString(bsonCollation.getString("maxVariable").getValue()));
}
if (bsonCollation.containsKey("normalization")) {
builder.normalization(bsonCollation.getBoolean("normalization").getValue());
}
if (bsonCollation.containsKey("backwards")) {
builder.backwards(bsonCollation.getBoolean("backwards").getValue());
}
return builder.build();
}
}