diff --git a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java index 6bbf9233cb1..7ed6ad3c369 100644 --- a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java +++ b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java @@ -88,6 +88,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import static com.mongodb.assertions.Assertions.assertNotNull; import static com.mongodb.connection.ClusterConnectionMode.LOAD_BALANCED; @@ -278,6 +279,11 @@ public static String getEnv(final String name, final String defaultValue) { return value == null ? defaultValue : value; } + public static Optional cryptSharedLibPathSysPropValue() { + String value = getEnv("CRYPT_SHARED_LIB_PATH", ""); + return value.isEmpty() ? Optional.empty() : Optional.of(value); + } + @Nullable public static String getEnv(final String name) { return System.getenv(name); diff --git a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java index 3e58712ca9c..0d8968eea90 100644 --- a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java +++ b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java @@ -176,6 +176,14 @@ public void create(final WriteConcern writeConcern, final BsonDocument createOpt case "size": createCollectionOptions.sizeInBytes(createOptions.getNumber("size").longValue()); break; + case "encryptedFields": + createCollectionOptions.encryptedFields(createOptions.getDocument("encryptedFields")); + break; + case "validator": + ValidationOptions validationOptions = new ValidationOptions(); + validationOptions.validator(createOptions.getDocument("validator")); + createCollectionOptions.validationOptions(validationOptions); + break; default: throw new UnsupportedOperationException("Unsupported create collection option: " + option); } @@ -195,6 +203,10 @@ public void create(final String collectionName, final CreateCollectionOptions op if (indexOptionDefaults.getStorageEngine() != null) { operation.indexOptionDefaults(new BsonDocument("storageEngine", toBsonDocument(indexOptionDefaults.getStorageEngine()))); } + Bson encryptedFields = options.getEncryptedFields(); + if (encryptedFields != null) { + operation.encryptedFields(encryptedFields.toBsonDocument()); + } ValidationOptions validationOptions = options.getValidationOptions(); if (validationOptions.getValidator() != null) { operation.validator(toBsonDocument(validationOptions.getValidator())); diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java b/driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java index 7a11b360046..edf1babd028 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/TestClusterListener.java @@ -32,7 +32,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; -import static com.mongodb.assertions.Assertions.isTrue; import static com.mongodb.assertions.Assertions.notNull; import static com.mongodb.internal.Locks.withLock; @@ -48,15 +47,17 @@ public final class TestClusterListener implements ClusterListener { @Override public void clusterOpening(final ClusterOpeningEvent event) { - isTrue("clusterOpeningEvent is null", clusterOpeningEvent == null); - clusterOpeningEvent = event; + if (clusterOpeningEvent == null) { + clusterOpeningEvent = event; + } } @Override public void clusterClosed(final ClusterClosedEvent event) { - isTrue("clusterClosingEvent is null", clusterClosingEvent == null); - closedLatch.countDown(); - clusterClosingEvent = event; + if (clusterClosingEvent == null) { + closedLatch.countDown(); + clusterClosingEvent = event; + } } @Override diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncClientEncryption.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncClientEncryption.scala new file mode 100644 index 00000000000..bb2987964db --- /dev/null +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncClientEncryption.scala @@ -0,0 +1,98 @@ +/* + * Copyright 2008-present 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 org.mongodb.scala.syncadapter + +import com.mongodb.ClusterFixture.TIMEOUT_DURATION +import com.mongodb.client.model.{ CreateCollectionOptions, CreateEncryptedCollectionParams } +import com.mongodb.client.model.vault.{ + DataKeyOptions, + EncryptOptions, + RewrapManyDataKeyOptions, + RewrapManyDataKeyResult +} +import com.mongodb.client.result.DeleteResult +import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption } +import com.mongodb.client.{ MongoDatabase => JMongoDatabase } +import org.bson.{ BsonBinary, BsonDocument, BsonValue } +import org.bson.conversions.Bson +import org.mongodb.scala.vault.ClientEncryption +import reactor.core.publisher.Mono + +import java.util.Objects.requireNonNull + +case class SyncClientEncryption(wrapped: ClientEncryption) extends JClientEncryption { + + override def createDataKey(kmsProvider: String): BsonBinary = + requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, new DataKeyOptions)).block(TIMEOUT_DURATION)) + + override def createDataKey(kmsProvider: String, dataKeyOptions: DataKeyOptions): BsonBinary = + requireNonNull(Mono.from(wrapped.createDataKey(kmsProvider, dataKeyOptions)).block(TIMEOUT_DURATION)) + + override def encrypt(value: BsonValue, options: EncryptOptions): BsonBinary = + requireNonNull(Mono.from(wrapped.encrypt(value, options)).block(TIMEOUT_DURATION)) + + override def encryptExpression(expression: Bson, options: EncryptOptions): BsonDocument = + requireNonNull(Mono.from(wrapped + .encryptExpression(expression.toBsonDocument, options)).block(TIMEOUT_DURATION).toBsonDocument) + + override def decrypt(value: BsonBinary): BsonValue = + requireNonNull(Mono.from(wrapped.decrypt(value)).block(TIMEOUT_DURATION)) + + override def deleteKey(id: BsonBinary): DeleteResult = + requireNonNull(Mono.from(wrapped.deleteKey(id)).block(TIMEOUT_DURATION)) + + override def getKey(id: BsonBinary): BsonDocument = Mono.from(wrapped.getKey(id)).block(TIMEOUT_DURATION) + + override def getKeys = new SyncFindIterable[BsonDocument](wrapped.keys) + + override def addKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument = + Mono.from(wrapped.addKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION) + + override def removeKeyAltName(id: BsonBinary, keyAltName: String): BsonDocument = + Mono.from(wrapped.removeKeyAltName(id, keyAltName)).block(TIMEOUT_DURATION) + + override def getKeyByAltName(keyAltName: String): BsonDocument = + Mono.from(wrapped.getKeyByAltName(keyAltName)).block(TIMEOUT_DURATION) + + override def rewrapManyDataKey(filter: Bson): RewrapManyDataKeyResult = + requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter)).block(TIMEOUT_DURATION)) + + override def rewrapManyDataKey(filter: Bson, options: RewrapManyDataKeyOptions): RewrapManyDataKeyResult = + requireNonNull(Mono.from(wrapped.rewrapManyDataKey(filter, options)).block(TIMEOUT_DURATION)) + + override def createEncryptedCollection( + database: JMongoDatabase, + collectionName: String, + createCollectionOptions: CreateCollectionOptions, + createEncryptedCollectionParams: CreateEncryptedCollectionParams + ): BsonDocument = { + database match { + case syncMongoDatabase: SyncMongoDatabase => + requireNonNull(Mono.from(wrapped.createEncryptedCollection( + syncMongoDatabase.wrapped, + collectionName, + createCollectionOptions, + createEncryptedCollectionParams + )).block(TIMEOUT_DURATION)) + case _ => throw new AssertionError(s"Unexpected database type: ${database.getClass}") + } + } + + override def close(): Unit = { + wrapped.close() + } +} diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoCollection.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoCollection.scala index 7d97d794c42..cc06b5f1a09 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoCollection.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoCollection.scala @@ -33,7 +33,6 @@ import org.mongodb.scala.MongoCollection import org.mongodb.scala.bson.DefaultHelper.DefaultsTo import org.mongodb.scala.result.{ InsertManyResult, InsertOneResult } -import java.util import java.util.concurrent.TimeUnit import scala.collection.JavaConverters._ import scala.concurrent.duration.{ Duration, MILLISECONDS } @@ -588,18 +587,18 @@ case class SyncMongoCollection[T](wrapped: MongoCollection[T]) extends JMongoCol ) override def renameCollection(newCollectionNamespace: MongoNamespace): Unit = { - throw new UnsupportedOperationException + wrapped.renameCollection(newCollectionNamespace).toFuture().get() } override def renameCollection( newCollectionNamespace: MongoNamespace, renameCollectionOptions: RenameCollectionOptions ): Unit = { - throw new UnsupportedOperationException + wrapped.renameCollection(newCollectionNamespace, renameCollectionOptions).toFuture().get() } override def renameCollection(clientSession: ClientSession, newCollectionNamespace: MongoNamespace): Unit = { - throw new UnsupportedOperationException + wrapped.renameCollection(unwrap(clientSession), newCollectionNamespace).toFuture().get() } override def renameCollection( @@ -607,6 +606,6 @@ case class SyncMongoCollection[T](wrapped: MongoCollection[T]) extends JMongoCol newCollectionNamespace: MongoNamespace, renameCollectionOptions: RenameCollectionOptions ): Unit = { - throw new UnsupportedOperationException + wrapped.renameCollection(unwrap(clientSession), newCollectionNamespace, renameCollectionOptions).toFuture().get() } } diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala index 548289fd938..846aa6580dc 100644 --- a/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/syncadapter/SyncMongoDatabase.scala @@ -51,13 +51,15 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase { override def withCodecRegistry(codecRegistry: CodecRegistry) = SyncMongoDatabase(wrapped.withCodecRegistry(codecRegistry)) - override def withReadPreference(readPreference: ReadPreference) = throw new UnsupportedOperationException + override def withReadPreference(readPreference: ReadPreference) = + SyncMongoDatabase(wrapped.withReadPreference(readPreference)) - override def withWriteConcern(writeConcern: WriteConcern) = throw new UnsupportedOperationException + override def withWriteConcern(writeConcern: WriteConcern) = SyncMongoDatabase(wrapped.withWriteConcern(writeConcern)) - override def withReadConcern(readConcern: ReadConcern) = throw new UnsupportedOperationException + override def withReadConcern(readConcern: ReadConcern) = SyncMongoDatabase(wrapped.withReadConcern(readConcern)) - override def withTimeout(timeout: Long, timeUnit: TimeUnit) = throw new UnsupportedOperationException + override def withTimeout(timeout: Long, timeUnit: TimeUnit) = + SyncMongoDatabase(wrapped.withTimeout(timeout, timeUnit)) override def getCollection(collectionName: String) = SyncMongoCollection[Document](wrapped.getCollection(collectionName)) @@ -170,7 +172,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase { } override def createView(viewName: String, viewOn: String, pipeline: java.util.List[_ <: Bson]): Unit = { - throw new UnsupportedOperationException + wrapped.createView(viewName, viewOn, pipeline.asScala.toList).toFuture().get() } override def createView( @@ -179,7 +181,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase { pipeline: java.util.List[_ <: Bson], createViewOptions: CreateViewOptions ): Unit = { - throw new UnsupportedOperationException + wrapped.createView(viewName, viewOn, pipeline.asScala.toList, createViewOptions).toFuture().get() } override def createView( @@ -188,7 +190,7 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase { viewOn: String, pipeline: java.util.List[_ <: Bson] ): Unit = { - throw new UnsupportedOperationException + wrapped.createView(unwrap(clientSession), viewName, viewOn, pipeline.asScala.toList).toFuture().get() } override def createView( @@ -198,7 +200,13 @@ case class SyncMongoDatabase(wrapped: MongoDatabase) extends JMongoDatabase { pipeline: java.util.List[_ <: Bson], createViewOptions: CreateViewOptions ): Unit = { - throw new UnsupportedOperationException + wrapped.createView( + unwrap(clientSession), + viewName, + viewOn, + pipeline.asScala.toList, + createViewOptions + ).toFuture().get() } override def watch = new SyncChangeStreamIterable[Document](wrapped.watch[Document]()) diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/ClientEncryptionTest.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/ClientEncryptionTest.scala new file mode 100644 index 00000000000..2b18a20e953 --- /dev/null +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/ClientEncryptionTest.scala @@ -0,0 +1,21 @@ +/* + * Copyright 2008-present 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 org.mongodb.scala.unified + +object ClientEncryptionTest extends UnifiedTest { + val directory = "client-side-encryption/tests/unified" +} diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedCrudTest.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedCrudTest.scala new file mode 100644 index 00000000000..6f58161ce12 --- /dev/null +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedCrudTest.scala @@ -0,0 +1,21 @@ +/* + * Copyright 2008-present 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 org.mongodb.scala.unified + +object UnifiedCrudTest extends UnifiedTest { + val directory = "crud" +} diff --git a/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedTest.scala b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedTest.scala new file mode 100644 index 00000000000..4b7d7e8f6e6 --- /dev/null +++ b/driver-scala/src/integrationTest/scala/org/mongodb/scala/unified/UnifiedTest.scala @@ -0,0 +1,66 @@ +/* + * Copyright 2008-present 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 org.mongodb.scala.unified + +import com.mongodb.client.gridfs.{ GridFSBucket => JGridFSBucket } +import com.mongodb.client.unified.UnifiedTest.Language +import com.mongodb.client.unified.{ UnifiedTest, UnifiedTest => JUnifiedTest } +import com.mongodb.client.vault.{ ClientEncryption => JClientEncryption } +import com.mongodb.client.{ MongoClient => JMongoClient, MongoDatabase => JMongoDatabase } +import com.mongodb.reactivestreams.client.internal.vault.ClientEncryptionImpl +import com.mongodb.{ ClientEncryptionSettings => JClientEncryptionSettings, MongoClientSettings } +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle +import org.junit.jupiter.params.provider.Arguments +import org.mongodb.scala.MongoClient +import org.mongodb.scala.MongoClient.DEFAULT_CODEC_REGISTRY +import org.mongodb.scala.syncadapter.{ SyncClientEncryption, SyncMongoClient } +import org.mongodb.scala.vault.ClientEncryption + +import java.util + +@TestInstance(Lifecycle.PER_CLASS) +abstract class UnifiedTest extends JUnifiedTest { + + val directory: String + + def data(): util.Collection[Arguments] = JUnifiedTest.getTestData(directory, true, Language.SCALA) + + override def createMongoClient(settings: MongoClientSettings): JMongoClient = + SyncMongoClient(MongoClient(MongoClientSettings.builder(settings).codecRegistry(DEFAULT_CODEC_REGISTRY).build())) + + override def createGridFSBucket(database: JMongoDatabase): JGridFSBucket = + throw new NotImplementedError("Not implemented") + + override def createClientEncryption( + keyVaultClient: JMongoClient, + clientEncryptionSettings: JClientEncryptionSettings + ): JClientEncryption = { + keyVaultClient match { + case client: SyncMongoClient => + SyncClientEncryption(ClientEncryption(new ClientEncryptionImpl( + client.wrapped.wrapped, + clientEncryptionSettings + ))) + case _ => throw new IllegalArgumentException(s"Invalid keyVaultClient type: ${keyVaultClient.getClass}") + } + } + + override protected def isReactive: Boolean = true + + override protected def getLanguage: Language = Language.SCALA +} diff --git a/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala b/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala index ba4510d308d..f2c8e4a74cb 100644 --- a/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala +++ b/driver-scala/src/main/scala/org/mongodb/scala/MongoClient.scala @@ -111,7 +111,7 @@ object MongoClient { * @param wrapped the underlying java MongoClient * @since 1.0 */ -case class MongoClient(private val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable { +case class MongoClient(protected[scala] val wrapped: JMongoClient) extends MongoCluster(wrapped) with Closeable { /** * Close the client, which will close all underlying cached resources, including, for example, diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotCreateMongocryptdClientTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotCreateMongocryptdClientTest.java index c8188c67657..897c7cab503 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotCreateMongocryptdClientTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotCreateMongocryptdClientTest.java @@ -44,7 +44,7 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Stream; -import static com.mongodb.client.AbstractClientSideEncryptionTest.cryptSharedLibPathSysPropValue; +import static com.mongodb.ClusterFixture.cryptSharedLibPathSysPropValue; import static com.mongodb.client.Fixture.getMongoClientSettings; import static com.mongodb.client.unified.UnifiedClientEncryptionHelper.localKmsProviderKey; import static java.lang.Math.toIntExact; diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotSpawnMongocryptdTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotSpawnMongocryptdTest.java index 33ab2b290e1..7f0b6995982 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotSpawnMongocryptdTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideEncryptionNotSpawnMongocryptdTest.java @@ -41,8 +41,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.mongodb.ClusterFixture.cryptSharedLibPathSysPropValue; import static com.mongodb.client.AbstractClientSideEncryptionNotCreateMongocryptdClientTest.findAvailableMongocryptdLoopbackPort; -import static com.mongodb.client.AbstractClientSideEncryptionTest.cryptSharedLibPathSysPropValue; import static com.mongodb.client.Fixture.getMongoClientSettings; import static com.mongodb.client.unified.UnifiedClientEncryptionHelper.localKmsProviderKey; import static java.lang.String.format; diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index 127ea91432b..35f1e93f775 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -16,6 +16,7 @@ package com.mongodb.client.unified; +import com.mongodb.AutoEncryptionSettings; import com.mongodb.ClientEncryptionSettings; import com.mongodb.ClientSessionOptions; import com.mongodb.ConnectionString; @@ -87,8 +88,8 @@ public final class Entities { private static final Set SUPPORTED_CLIENT_ENTITY_OPTIONS = new HashSet<>( asList( - "id", "uriOptions", "serverApi", "useMultipleMongoses", "observeEvents", - "observeLogMessages", "observeSensitiveCommands", "ignoreCommandMonitoringEvents")); + "id", "autoEncryptOpts", "uriOptions", "serverApi", "useMultipleMongoses", "storeEventsAsEntities", + "observeEvents", "observeLogMessages", "observeSensitiveCommands", "ignoreCommandMonitoringEvents")); private final Set entityNames = new HashSet<>(); private final Map threads = new HashMap<>(); private final Map>> tasks = new HashMap<>(); @@ -499,6 +500,59 @@ private void initClient(final BsonDocument entity, final String id, } clientSettingsBuilder.serverApi(serverApiBuilder.build()); } + if (entity.containsKey("autoEncryptOpts")) { + AutoEncryptionSettings.Builder builder = AutoEncryptionSettings.builder(); + for (Map.Entry entry : entity.getDocument("autoEncryptOpts").entrySet()) { + switch (entry.getKey()) { + case "bypassAutoEncryption": + builder.bypassAutoEncryption(entry.getValue().asBoolean().getValue()); + break; + case "bypassQueryAnalysis": + builder.bypassQueryAnalysis(entry.getValue().asBoolean().getValue()); + break; + case "schemaMap": + Map schemaMap = new HashMap<>(); + for (Map.Entry entries : entry.getValue().asDocument().entrySet()) { + schemaMap.put(entries.getKey(), entries.getValue().asDocument()); + } + builder.schemaMap(schemaMap); + break; + case "encryptedFieldsMap": + Map encryptedFieldsMap = new HashMap<>(); + for (Map.Entry entries : entry.getValue().asDocument().entrySet()) { + encryptedFieldsMap.put(entries.getKey(), entries.getValue().asDocument()); + } + builder.encryptedFieldsMap(encryptedFieldsMap); + break; + case "extraOptions": + Map extraOptions = new HashMap<>(); + for (Map.Entry extraOptionsEntry : entry.getValue().asDocument().entrySet()) { + switch (extraOptionsEntry.getKey()) { + case "mongocryptdBypassSpawn": + extraOptions.put(extraOptionsEntry.getKey(), extraOptionsEntry.getValue().asBoolean().getValue()); + break; + default: + throw new UnsupportedOperationException("Unsupported extra encryption option: " + extraOptionsEntry.getKey()); + } + } + builder.extraOptions(extraOptions); + break; + case "keyVaultNamespace": + builder.keyVaultNamespace(entry.getValue().asString().getValue()); + break; + case "kmsProviders": + builder.kmsProviders(createKmsProvidersMap(entry.getValue().asDocument())); + break; + case "keyExpirationMS": + builder.keyExpiration(entry.getValue().asNumber().longValue(), TimeUnit.MILLISECONDS); + break; + default: + throw new UnsupportedOperationException("Unsupported client encryption option: " + entry.getKey()); + } + } + clientSettingsBuilder.autoEncryptionSettings(builder.build()); + } + MongoClientSettings clientSettings = clientSettingsBuilder.build(); if (entity.containsKey("observeLogMessages")) { diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedClientEncryptionHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedClientEncryptionHelper.java index 59f96aa9492..c9c0d380c5b 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedClientEncryptionHelper.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedClientEncryptionHelper.java @@ -98,8 +98,7 @@ static Map> createKmsProvidersMap(final BsonDocument kmsProviderMap, kmsProviderOptions, "endpoint", - () -> getEnv("org.mongodb.test.kmipEndpoint", "localhost:5698"), - null); + () -> getEnv("org.mongodb.test.kmipEndpoint", "localhost:5698")); break; case "local": case "local:name1": @@ -107,16 +106,14 @@ static Map> createKmsProvidersMap(final BsonDocument kmsProviderMap, kmsProviderOptions, "key", - UnifiedClientEncryptionHelper::localKmsProviderKey, - null); + UnifiedClientEncryptionHelper::localKmsProviderKey); break; case "local:name2": setKmsProviderProperty( kmsProviderMap, kmsProviderOptions, "key", - null, - () -> decodeLocalKmsProviderKey(kmsProviderOptions.getString("key").getValue())); + () -> decodeKmsProviderString(kmsProviderOptions.getString("key").getValue())); break; default: throw new UnsupportedOperationException("Unsupported KMS provider: " + kmsProviderKey); @@ -127,15 +124,14 @@ static Map> createKmsProvidersMap(final BsonDocument } public static byte[] localKmsProviderKey() { - return decodeLocalKmsProviderKey("Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZ" + return decodeKmsProviderString("Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZ" + "GJkTXVyZG9uSjFk"); } - public static byte[] decodeLocalKmsProviderKey(final String key) { + public static byte[] decodeKmsProviderString(final String key) { return Base64.getDecoder().decode(key); } - private static void setKmsProviderProperty(final Map kmsProviderMap, final BsonDocument kmsProviderOptions, final String key, final String propertyName) { setKmsProviderProperty( @@ -147,14 +143,12 @@ private static void setKmsProviderProperty(final Map kmsProvider return getEnv(propertyName); } throw new UnsupportedOperationException("Missing system property for: " + key); - }, - null); + }); } private static void setKmsProviderProperty(final Map kmsProviderMap, final BsonDocument kmsProviderOptions, final String key, - @Nullable final Supplier placeholderPropertySupplier, - @Nullable final Supplier explicitPropertySupplier) { + @Nullable final Supplier placeholderPropertySupplier) { if (kmsProviderOptions.containsKey(key)) { boolean isPlaceholderValue = kmsProviderOptions.get(key).equals(PLACEHOLDER); if (isPlaceholderValue) { @@ -165,10 +159,11 @@ private static void setKmsProviderProperty(final Map kmsProvider return; } - if (explicitPropertySupplier == null) { - kmsProviderMap.put(key, kmsProviderOptions.get(key)); + BsonValue kmsValue = kmsProviderOptions.get(key); + if (kmsValue.isString()) { + kmsProviderMap.put(key, decodeKmsProviderString(kmsValue.asString().getValue())); } else { - kmsProviderMap.put(key, explicitPropertySupplier.get()); + kmsProviderMap.put(key, kmsValue); } } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java index d3945221e14..aa220d75f72 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java @@ -57,6 +57,7 @@ import com.mongodb.client.model.DeleteManyModel; import com.mongodb.client.model.DeleteOneModel; import com.mongodb.client.model.DeleteOptions; +import com.mongodb.client.model.DropCollectionOptions; import com.mongodb.client.model.DropIndexOptions; import com.mongodb.client.model.EstimatedDocumentCountOptions; import com.mongodb.client.model.FindOneAndDeleteOptions; @@ -77,6 +78,7 @@ import com.mongodb.client.model.UpdateManyModel; import com.mongodb.client.model.UpdateOneModel; import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.ValidationOptions; import com.mongodb.client.model.WriteModel; import com.mongodb.client.model.bulk.ClientBulkWriteOptions; import com.mongodb.client.model.bulk.ClientBulkWriteResult; @@ -1362,14 +1364,19 @@ OperationResult executeWithTransaction(final BsonDocument operation, final Opera public OperationResult executeDropCollection(final BsonDocument operation) { MongoDatabase database = getMongoDatabase(operation); BsonDocument arguments = operation.getDocument("arguments", new BsonDocument()); - String collectionName = arguments.getString("collection").getValue(); + String collectionName = arguments.remove("collection").asString().getValue(); - if (operation.getDocument("arguments").size() > 1) { - throw new UnsupportedOperationException("Unexpected arguments " + operation.get("arguments")); + DropCollectionOptions dropCollectionOptions = new DropCollectionOptions(); + for (Map.Entry entry : arguments.entrySet()) { + if (entry.getKey().equals("encryptedFields")) { + dropCollectionOptions.encryptedFields(entry.getValue().asDocument()); + } else { + throw new UnsupportedOperationException("Unsupported drop collections option: " + entry.getKey()); + } } return resultOf(() -> { - database.getCollection(collectionName).drop(); + database.getCollection(collectionName).drop(dropCollectionOptions); return null; }); } @@ -1429,6 +1436,14 @@ public OperationResult executeCreateCollection(final BsonDocument operation) { case "clusteredIndex": options.clusteredIndexOptions(createClusteredIndexOptions(cur.getValue().asDocument())); break; + case "encryptedFields": + options.encryptedFields(cur.getValue().asDocument()); + break; + case "validator": + ValidationOptions validationOptions = new ValidationOptions(); + validationOptions.validator(cur.getValue().asDocument()); + options.validationOptions(validationOptions); + break; default: throw new UnsupportedOperationException("Unsupported argument: " + cur.getKey()); } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java index 3364cf4183d..1d62d0475ad 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java @@ -110,7 +110,7 @@ public abstract class UnifiedTest { private static final Set PRESTART_POOL_ASYNC_WORK_MANAGER_FILE_DESCRIPTIONS = Collections.singleton( "wait queue timeout errors include details about checked out connections"); - private static final String MAX_SUPPORTED_SCHEMA_VERSION = "1.22"; + private static final String MAX_SUPPORTED_SCHEMA_VERSION = "1.23"; private static final List MAX_SUPPORTED_SCHEMA_VERSION_COMPONENTS = Arrays.stream(MAX_SUPPORTED_SCHEMA_VERSION.split("\\.")) .map(Integer::parseInt) .collect(Collectors.toList()); @@ -1103,6 +1103,6 @@ protected void ignoreExtraEvents() { } public enum Language { - JAVA, KOTLIN + JAVA, KOTLIN, SCALA } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java index 6d559e0d666..0b209d85f70 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java @@ -253,6 +253,10 @@ public static void applyCustomizations(final TestDef def) { .when(() -> def.isReactive() && UnifiedTest.Language.KOTLIN.equals(def.getLanguage())) .file("crud", "findOne"); + def.skipNoncompliant("Scala Mono pulls the data and sets the batch size https://jira.mongodb.org/browse/JAVA-5838") + .when(() -> UnifiedTest.Language.SCALA.equals(def.getLanguage())) + .file("crud", "findOne"); + def.skipNoncompliant("Updates and Replace bulk operations are split in the java driver") .file("crud", "bulkWrite-comment");