diff --git a/.evergreen/.evg.yml b/.evergreen/.evg.yml index 40c104ddbf2..9187ff62edb 100644 --- a/.evergreen/.evg.yml +++ b/.evergreen/.evg.yml @@ -289,6 +289,16 @@ functions: ${PREPARE_SHELL} SCALA="${SCALA}" AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" SAFE_FOR_MULTI_MONGOS="${SAFE_FOR_MULTI_MONGOS}" TOPOLOGY="${TOPOLOGY}" JAVA_VERSION="${JAVA_VERSION}" .evergreen/run-scala-tests.sh + "run kotlin tests": + - command: shell.exec + type: test + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + AUTH="${AUTH}" SSL="${SSL}" MONGODB_URI="${MONGODB_URI}" SAFE_FOR_MULTI_MONGOS="${SAFE_FOR_MULTI_MONGOS}" TOPOLOGY="${TOPOLOGY}" JAVA_VERSION="${JAVA_VERSION}" .evergreen/run-kotlin-tests.sh + + "run socket tests": - command: shell.exec type: test @@ -904,6 +914,11 @@ tasks: - func: "bootstrap mongo-orchestration" - func: "run scala tests" + - name: "kotlin-tests" + commands: + - func: "bootstrap mongo-orchestration" + - func: "run kotlin tests" + - name: "reactive-streams-tck-test" commands: - func: "bootstrap mongo-orchestration" @@ -1437,6 +1452,8 @@ tasks: name: "plain-auth-test" - variant: ".test-scala-variant" name: "scala-tests" + - variant: ".test-kotlin-variant" + name: "kotlin-tests" - variant: ".tests-netty-variant" name: "netty-test" commands: @@ -2052,6 +2069,13 @@ buildvariants: tasks: - name: "scala-tests" +- matrix_name: "kotlin-tests" + matrix_spec: { auth: "noauth", ssl: "nossl", jdk: ["jdk8", "jdk17"], version: ["6.0"], topology: "replicaset", os: "ubuntu" } + display_name: "Kotlin: ${jdk} ${version} ${topology} ${os}" + tags: ["test-kotlin-variant"] + tasks: + - name: "kotlin-tests" + - name: publish-snapshot display_name: "Publish Snapshot" run_on: ubuntu1804-test diff --git a/.evergreen/run-kotlin-tests.sh b/.evergreen/run-kotlin-tests.sh new file mode 100755 index 00000000000..836a48516b7 --- /dev/null +++ b/.evergreen/run-kotlin-tests.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -o xtrace # Write all commands first to stderr +set -o errexit # Exit the script with error if any of the commands fail + + +AUTH=${AUTH:-noauth} +SSL=${SSL:-nossl} +MONGODB_URI=${MONGODB_URI:-} +TOPOLOGY=${TOPOLOGY:-standalone} +SAFE_FOR_MULTI_MONGOS=${SAFE_FOR_MULTI_MONGOS:-} + +############################################ +# Main Program # +############################################ +RELATIVE_DIR_PATH="$(dirname "${BASH_SOURCE:-$0}")" +. "${RELATIVE_DIR_PATH}/javaConfig.bash" + + +if [ "$SSL" != "nossl" ]; then + echo -e "\nSSL support not configured for Kotlin tests" + exit 1 +fi + +if [ "$AUTH" != "noauth" ]; then + echo -e "\nAuth support not configured for Kotlin tests" + exit 1 +fi + +if [ "$SAFE_FOR_MULTI_MONGOS" == "true" ]; then + export MULTI_MONGOS_URI_SYSTEM_PROPERTY="-Dorg.mongodb.test.multi.mongos.uri=${MONGODB_URI}" +fi + +echo "Running Kotlin tests" + +./gradlew -version +./gradlew :driver-kotlin-sync:kCheck -Dorg.mongodb.test.uri=${MONGODB_URI} ${MULTI_MONGOS_URI_SYSTEM_PROPERTY} diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml new file mode 100644 index 00000000000..2297ba60044 --- /dev/null +++ b/config/detekt/baseline.xml @@ -0,0 +1,15 @@ + + + + + IteratorNotThrowingNoSuchElementException:MongoCursor.kt$MongoCursor<T : Any> : IteratorCloseable + LargeClass:MongoCollectionTest.kt$MongoCollectionTest + LongMethod:FindIterableTest.kt$FindIterableTest$@Suppress("DEPRECATION") @Test fun shouldCallTheUnderlyingMethods() + MaxLineLength:MapReduceIterable.kt$MapReduceIterable$* + SwallowedException:MockitoHelper.kt$MockitoHelper.DeepReflectionEqMatcher$e: Throwable + TooManyFunctions:ClientSession.kt$ClientSession : jClientSession + TooManyFunctions:FindIterable.kt$FindIterable<T : Any> : MongoIterable + TooManyFunctions:MongoCollection.kt$MongoCollection<T : Any> + TooManyFunctions:MongoDatabase.kt$MongoDatabase + + diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml new file mode 100644 index 00000000000..4c083b0bce5 --- /dev/null +++ b/config/detekt/detekt.yml @@ -0,0 +1,712 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false + CommentOverPrivateFunction: + active: false + CommentOverPrivateProperty: + active: false + DeprecatedBlockTag: + active: false + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false + UndocumentedPublicClass: + active: false + excludes: ['**/test/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**'] + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**'] + +complexity: + active: true + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + ComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: false + ignoredLabels: [] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + ignoreArgumentsMatchingNames: false + NestedBlockDepth: + active: true + threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: ['**/test/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: ['**/test/**'] + thresholdInFiles: 25 + thresholdInClasses: 25 + thresholdInInterfaces: 25 + thresholdInObjects: 25 + thresholdInEnums: 25 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: false + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + excludes: ['**/test/**'] + NotImplementedDeclaration: + active: false + ObjectExtendsThrowable: + active: false + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: false + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: true + excludes: ['**/test/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: true + excludes: ['**/test/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: false + allowedPattern: '^(is|has|are)' + ignoreOverridden: true + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: false + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + InvalidPackageDeclaration: + active: true + rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: false + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: false + maximumVariableNameLength: 64 + VariableMinLength: + active: false + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + ignoreOverridden: true + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: false + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**'] + SpreadOperator: + active: true + excludes: ['**/test/**'] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastToNullableType: + active: false + Deprecation: + active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + DuplicateCaseInWhenExpression: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: false + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToAnnotatedMethods: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: false + excludes: ['**/*.kts'] + MissingWhenCase: + active: true + allowElseExpression: true + NullCheckOnMutableProperty: + active: false + NullableToStringCall: + active: false + RedundantElseInWhen: + active: true + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**'] + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + CanBeNonNullable: + active: false + CascadingCallWrapping: + active: false + includeElvis: true + ClassOrdering: + active: false + CollapsibleIfStatements: + active: false + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: 'to' + DataClassShouldBeImmutable: + active: false + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: false + ExplicitCollectionElementAccessMethod: + active: false + ExplicitItLambdaParameter: + active: true + ExpressionBodySyntax: + active: false + includeLineWrapping: false + ForbiddenComment: + active: true + values: + - 'FIXME:' + - 'STOPSHIP:' + - 'TODO:' + allowedPatterns: '' + customMessage: '' + ForbiddenImport: + active: false + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - 'kotlin.io.print' + - 'kotlin.io.println' + ForbiddenPublicDataClass: + active: true + excludes: ['**'] + ignorePackages: + - '*.internal' + - '*.internal.*' + ForbiddenSuppress: + active: false + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: false + ignoreUsageInGenerics: false + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: '' + LibraryCodeMustSpecifyReturnType: + active: true + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: ['**/test/**', '**/*.kts'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: false + ignoreNamedArgument: true + ignoreEnums: true + ignoreRanges: false + ignoreExtensionFunctions: true + MandatoryBracesIfStatements: + active: false + MandatoryBracesLoops: + active: false + MaxChainedCallsOnSameLine: + active: false + maxChainedCalls: 5 + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: false + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: false + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: false + NullableBooleanCheck: + active: false + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: false + OptionalWhenBraces: + active: false + PreferToOverPairSyntax: + active: false + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: false + ReturnCount: + active: true + max: 2 + excludedFunctions: 'equals' + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: true + max: 2 + excludeGuardClauses: false + TrailingWhitespace: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 4 + allowNonStandardGrouping: false + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: false + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: false + UnnecessaryLet: + active: false + UnnecessaryParentheses: + active: false + UntilInsteadOfRangeTo: + active: false + UnusedImports: + active: false + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '(_|ignored|expected|serialVersionUID)' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: true + UseDataClass: + active: false + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + UseIsNullOrEmpty: + active: true + UseOrEmpty: + active: true + UseRequire: + active: true + UseRequireNotNull: + active: true + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + ignoreLateinitVar: false + WildcardImport: + active: true + excludes: ['**/test/**'] + excludeImports: + - 'java.util.*' diff --git a/config/mongodb.license b/config/mongodb.license new file mode 100644 index 00000000000..6a2444433a7 --- /dev/null +++ b/config/mongodb.license @@ -0,0 +1,15 @@ +/* + * 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. + */ diff --git a/config/spotbugs/exclude.xml b/config/spotbugs/exclude.xml index 8cfa95fb4ec..5e095d213b2 100644 --- a/config/spotbugs/exclude.xml +++ b/config/spotbugs/exclude.xml @@ -170,6 +170,11 @@ + + + + + diff --git a/driver-kotlin-sync/build.gradle.kts b/driver-kotlin-sync/build.gradle.kts new file mode 100644 index 00000000000..32ace53c46f --- /dev/null +++ b/driver-kotlin-sync/build.gradle.kts @@ -0,0 +1,185 @@ +/* + * 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. + */ +import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("org.jetbrains.kotlin.jvm") version "1.8.10" + `java-library` + + // Test based plugins + id("com.diffplug.spotless") + id("org.jetbrains.dokka") version "1.7.20" + id("io.gitlab.arturbosch.detekt") version "1.21.0" +} + +repositories { + mavenCentral() + google() +} + +base.archivesName.set("mongodb-driver-kotlin-sync") + +description = "The MongoDB Kotlin Driver" + +ext.set("pomName", "MongoDB Kotlin Driver") + +sourceSets { + create("integrationTest") { + kotlin.srcDir("$projectDir/src/integration/kotlin") + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + compileClasspath += project(":driver-sync").sourceSets.test.get().output + runtimeClasspath += project(":driver-sync").sourceSets.test.get().output + compileClasspath += project(":driver-core").sourceSets.test.get().output + runtimeClasspath += project(":driver-core").sourceSets.test.get().output + compileClasspath += project(":bson").sourceSets.test.get().output + runtimeClasspath += project(":bson").sourceSets.test.get().output + } +} + +val integrationTestImplementation: Configuration by + configurations.getting { extendsFrom(configurations.testImplementation.get()) } + +dependencies { + // Align versions of all Kotlin components + implementation(platform("org.jetbrains.kotlin:kotlin-bom")) + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + + api(project(path = ":bson", configuration = "default")) + api(project(path = ":driver-sync", configuration = "default")) + + testImplementation("org.jetbrains.kotlin:kotlin-reflect") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit") + testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") + testImplementation("org.mockito:mockito-junit-jupiter:4.11.0") + testImplementation("org.assertj:assertj-core:3.24.2") + testImplementation("io.github.classgraph:classgraph:4.8.154") + + integrationTestImplementation("org.jetbrains.kotlin:kotlin-test-junit") + integrationTestImplementation(project(path = ":driver-sync")) + integrationTestImplementation(project(path = ":driver-core")) + integrationTestImplementation(project(path = ":bson")) +} + +kotlin { explicitApi() } + +tasks.withType { kotlinOptions.jvmTarget = "1.8" } + +// =========================== +// Code Quality checks +// =========================== +spotless { + kotlinGradle { + ktfmt("0.39").dropboxStyle().configure { it.setMaxWidth(120) } + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + licenseHeaderFile(rootProject.file("config/mongodb.license"), "(group|plugins|import|buildscript|rootProject)") + } + + kotlin { + target("**/*.kt") + ktfmt().dropboxStyle().configure { it.setMaxWidth(120) } + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + licenseHeaderFile(rootProject.file("config/mongodb.license")) + } + + format("extraneous") { + target("*.xml", "*.yml", "*.md") + trimTrailingWhitespace() + indentWithSpaces() + endWithNewline() + } +} + +tasks.named("check") { dependsOn("spotlessApply") } + +detekt { + allRules = true // fail build on any finding + buildUponDefaultConfig = true // preconfigure defaults + config = rootProject.files("config/detekt/detekt.yml") // point to your custom config defining rules to run, + // overwriting default behavior + baseline = rootProject.file("config/detekt/baseline.xml") // a way of suppressing issues before introducing detekt + source = + files( + file("src/main/kotlin"), + file("src/test/kotlin"), + file("src/integrationTest/kotlin"), + ) +} + +tasks.withType().configureEach { + reports { + html.required.set(true) // observe findings in your browser with structure and code snippets + xml.required.set(true) // checkstyle like format mainly for integrations like Jenkins + txt.required.set(false) // similar to the console output, contains issue signature to manually edit + } +} + +spotbugs { + showProgress.set(true) + + tasks.getByName("spotbugsIntegrationTest") { enabled = false } +} + +// =========================== +// Test Configuration +// =========================== +val integrationTest = + tasks.create("integrationTest", Test::class) { + description = "Runs the integration tests." + group = "verification" + + testClassesDirs = sourceSets["integrationTest"].output.classesDirs + classpath = sourceSets["integrationTest"].runtimeClasspath + } + +tasks.create("kCheck") { + description = "Runs all the kotlin checks" + group = "verification" + + dependsOn("clean", "check", integrationTest) + tasks.findByName("check")?.mustRunAfter("clean") + tasks.findByName("integrationTest")?.mustRunAfter("check") +} + +tasks.test { useJUnitPlatform() } + +// =========================== +// Dokka Configuration +// =========================== +val dokkaOutputDir = "${rootProject.buildDir}/docs/${base.archivesName.get()}" + +tasks.dokkaHtml.configure { + outputDirectory.set(file(dokkaOutputDir)) + moduleName.set(base.archivesName.get()) +} + +val cleanDokka by tasks.register("cleanDokka") { delete(dokkaOutputDir) } + +project.parent?.tasks?.named("docs") { + dependsOn(tasks.dokkaHtml) + mustRunAfter(cleanDokka) +} + +tasks.javadocJar.configure { + dependsOn(cleanDokka, tasks.dokkaHtml) + archiveClassifier.set("javadoc") + from(dokkaOutputDir) +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/CrudTest.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/CrudTest.kt new file mode 100644 index 00000000000..f7a9c88c89a --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/CrudTest.kt @@ -0,0 +1,49 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.AbstractCrudTest +import com.mongodb.client.Fixture +import com.mongodb.client.MongoDatabase +import com.mongodb.event.CommandListener +import com.mongodb.kotlin.client.syncadapter.SyncMongoClient +import org.bson.BsonArray +import org.bson.BsonDocument + +data class CrudTest( + val filename: String, + val description: String, + val databaseName: String, + val collectionName: String, + val data: BsonArray, + val definition: BsonDocument, + val skipTest: Boolean +) : AbstractCrudTest(filename, description, databaseName, collectionName, data, definition, skipTest) { + + private var mongoClient: SyncMongoClient? = null + + override fun createMongoClient(commandListener: CommandListener) { + mongoClient = + SyncMongoClient( + MongoClient.create(Fixture.getMongoClientSettingsBuilder().addCommandListener(commandListener).build())) + } + + override fun getDatabase(databaseName: String): MongoDatabase = mongoClient!!.getDatabase(databaseName) + + override fun cleanUp() { + mongoClient?.close() + } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedCrudTest.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedCrudTest.kt new file mode 100644 index 00000000000..4818497a1c5 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedCrudTest.kt @@ -0,0 +1,52 @@ +/* + * 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 com.mongodb.kotlin.client + +import java.io.IOException +import java.net.URISyntaxException +import org.bson.BsonArray +import org.bson.BsonDocument +import org.junit.Assume.assumeFalse +import org.junit.runners.Parameterized + +internal class UnifiedCrudTest( + fileDescription: String?, + testDescription: String, + schemaVersion: String, + runOnRequirements: BsonArray?, + entitiesArray: BsonArray, + initialData: BsonArray, + definition: BsonDocument +) : UnifiedTest(fileDescription, schemaVersion, runOnRequirements, entitiesArray, initialData, definition) { + + init { + assumeFalse(testDescription == "Unacknowledged findOneAndReplace with hint string on 4.4+ server") + assumeFalse(testDescription == "Unacknowledged findOneAndReplace with hint document on 4.4+ server") + assumeFalse(testDescription == "Unacknowledged findOneAndUpdate with hint string on 4.4+ server") + assumeFalse(testDescription == "Unacknowledged findOneAndUpdate with hint document on 4.4+ server") + assumeFalse(testDescription == "Unacknowledged findOneAndDelete with hint string on 4.4+ server") + assumeFalse(testDescription == "Unacknowledged findOneAndDelete with hint document on 4.4+ server") + } + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}: {1}") + @Throws(URISyntaxException::class, IOException::class) + fun data(): Collection?>? { + return getTestData("unified-test-format/crud") + } + } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedTest.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedTest.kt new file mode 100644 index 00000000000..99a56849d7c --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/UnifiedTest.kt @@ -0,0 +1,51 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ClientEncryptionSettings +import com.mongodb.MongoClientSettings +import com.mongodb.client.MongoClient as JMongoClient +import com.mongodb.client.MongoDatabase as JMongoDatabase +import com.mongodb.client.gridfs.GridFSBucket +import com.mongodb.client.unified.UnifiedTest as JUnifiedTest +import com.mongodb.client.vault.ClientEncryption +import com.mongodb.kotlin.client.syncadapter.SyncMongoClient +import org.bson.BsonArray +import org.bson.BsonDocument + +internal abstract class UnifiedTest( + fileDescription: String?, + schemaVersion: String, + runOnRequirements: BsonArray?, + entitiesArray: BsonArray, + initialData: BsonArray, + definition: BsonDocument +) : JUnifiedTest(fileDescription, schemaVersion, runOnRequirements, entitiesArray, initialData, definition) { + + override fun createMongoClient(settings: MongoClientSettings): JMongoClient = + SyncMongoClient(MongoClient.create(settings)) + + override fun createGridFSBucket(database: JMongoDatabase?): GridFSBucket { + TODO("Not yet implemented - JAVA-4893") + } + + override fun createClientEncryption( + keyVaultClient: JMongoClient?, + clientEncryptionSettings: ClientEncryptionSettings? + ): ClientEncryption { + TODO("Not yet implemented - JAVA-4896") + } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncAggregateIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncAggregateIterable.kt new file mode 100644 index 00000000000..2640e6250d7 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncAggregateIterable.kt @@ -0,0 +1,69 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.ExplainVerbosity +import com.mongodb.client.AggregateIterable as JAggregateIterable +import com.mongodb.client.model.Collation +import com.mongodb.kotlin.client.AggregateIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.Document +import org.bson.conversions.Bson + +internal class SyncAggregateIterable(val wrapped: AggregateIterable) : + JAggregateIterable, SyncMongoIterable(wrapped) { + override fun batchSize(batchSize: Int): SyncAggregateIterable = apply { wrapped.batchSize(batchSize) } + + override fun toCollection() = wrapped.toCollection() + + override fun allowDiskUse(allowDiskUse: Boolean?): SyncAggregateIterable = apply { + wrapped.allowDiskUse(allowDiskUse) + } + + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncAggregateIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + override fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit): SyncAggregateIterable = apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + + override fun bypassDocumentValidation(bypassDocumentValidation: Boolean?): SyncAggregateIterable = apply { + wrapped.bypassDocumentValidation(bypassDocumentValidation) + } + + override fun collation(collation: Collation?): SyncAggregateIterable = apply { wrapped.collation(collation) } + + override fun comment(comment: String?): SyncAggregateIterable = apply { wrapped.comment(comment) } + + override fun comment(comment: BsonValue?): SyncAggregateIterable = apply { wrapped.comment(comment) } + + override fun hint(hint: Bson?): SyncAggregateIterable = apply { wrapped.hint(hint) } + + override fun hintString(hint: String?): SyncAggregateIterable = apply { wrapped.hintString(hint) } + + override fun let(variables: Bson?): SyncAggregateIterable = apply { wrapped.let(variables) } + + override fun explain(): Document = wrapped.explain() + + override fun explain(verbosity: ExplainVerbosity): Document = wrapped.explain(verbosity) + + override fun explain(explainResultClass: Class): E = wrapped.explain(explainResultClass) + + override fun explain(explainResultClass: Class, verbosity: ExplainVerbosity): E = + wrapped.explain(explainResultClass, verbosity) +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncChangeStreamIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncChangeStreamIterable.kt new file mode 100644 index 00000000000..0d579e68006 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncChangeStreamIterable.kt @@ -0,0 +1,61 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.ChangeStreamIterable as JChangeStreamIterable +import com.mongodb.client.MongoIterable +import com.mongodb.client.model.Collation +import com.mongodb.client.model.changestream.ChangeStreamDocument +import com.mongodb.client.model.changestream.FullDocument +import com.mongodb.client.model.changestream.FullDocumentBeforeChange +import com.mongodb.kotlin.client.ChangeStreamIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonDocument +import org.bson.BsonTimestamp +import org.bson.BsonValue + +internal class SyncChangeStreamIterable(val wrapped: ChangeStreamIterable) : + JChangeStreamIterable, SyncMongoIterable>(wrapped) { + override fun withDocumentClass(clazz: Class): MongoIterable = + SyncMongoIterable(wrapped.withDocumentClass(clazz)) + override fun batchSize(batchSize: Int): SyncChangeStreamIterable = apply { wrapped.batchSize(batchSize) } + override fun collation(collation: Collation?): SyncChangeStreamIterable = apply { wrapped.collation(collation) } + override fun comment(comment: BsonValue?): SyncChangeStreamIterable = apply { wrapped.comment(comment) } + override fun comment(comment: String?): SyncChangeStreamIterable = apply { wrapped.comment(comment) } + override fun cursor(): SyncMongoChangeStreamCursor> = + SyncMongoChangeStreamCursor(wrapped.cursor()) + override fun fullDocument(fullDocument: FullDocument): SyncChangeStreamIterable = apply { + wrapped.fullDocument(fullDocument) + } + override fun fullDocumentBeforeChange( + fullDocumentBeforeChange: FullDocumentBeforeChange + ): SyncChangeStreamIterable = apply { wrapped.fullDocumentBeforeChange(fullDocumentBeforeChange) } + override fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit): SyncChangeStreamIterable = apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + override fun resumeAfter(resumeToken: BsonDocument): SyncChangeStreamIterable = apply { + wrapped.resumeAfter(resumeToken) + } + override fun showExpandedEvents(showExpandedEvents: Boolean): SyncChangeStreamIterable = apply { + wrapped.showExpandedEvents(showExpandedEvents) + } + override fun startAfter(startAfter: BsonDocument): SyncChangeStreamIterable = apply { + wrapped.startAfter(startAfter) + } + override fun startAtOperationTime(startAtOperationTime: BsonTimestamp): SyncChangeStreamIterable = apply { + wrapped.startAtOperationTime(startAtOperationTime) + } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncClientSession.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncClientSession.kt new file mode 100644 index 00000000000..53d791bd423 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncClientSession.kt @@ -0,0 +1,93 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.ClientSessionOptions +import com.mongodb.ServerAddress +import com.mongodb.TransactionOptions +import com.mongodb.client.ClientSession as JClientSession +import com.mongodb.client.TransactionBody +import com.mongodb.kotlin.client.ClientSession +import com.mongodb.session.ServerSession +import org.bson.BsonDocument +import org.bson.BsonTimestamp + +internal class SyncClientSession(internal val wrapped: ClientSession, private val originator: Any) : JClientSession { + private val delegate: JClientSession = wrapped.wrapped + + override fun close(): Unit = delegate.close() + + override fun getPinnedServerAddress(): ServerAddress? = delegate.pinnedServerAddress + + override fun getTransactionContext(): Any? = delegate.transactionContext + + override fun setTransactionContext(address: ServerAddress, transactionContext: Any): Unit = + delegate.setTransactionContext(address, transactionContext) + + override fun clearTransactionContext(): Unit = delegate.clearTransactionContext() + + override fun getRecoveryToken(): BsonDocument? = delegate.recoveryToken + + override fun setRecoveryToken(recoveryToken: BsonDocument): Unit { + delegate.recoveryToken = recoveryToken + } + + override fun getOptions(): ClientSessionOptions = delegate.options + + override fun isCausallyConsistent(): Boolean = delegate.isCausallyConsistent + + override fun getOriginator(): Any = originator + + override fun getServerSession(): ServerSession = delegate.serverSession + + override fun getOperationTime(): BsonTimestamp = delegate.operationTime + + override fun advanceOperationTime(operationTime: BsonTimestamp?): Unit = + delegate.advanceOperationTime(operationTime) + + override fun advanceClusterTime(clusterTime: BsonDocument?): Unit = delegate.advanceClusterTime(clusterTime) + + override fun setSnapshotTimestamp(snapshotTimestamp: BsonTimestamp?) { + delegate.snapshotTimestamp = snapshotTimestamp + } + + override fun getSnapshotTimestamp(): BsonTimestamp? = delegate.snapshotTimestamp + + override fun getClusterTime(): BsonDocument = delegate.clusterTime + + override fun hasActiveTransaction(): Boolean = delegate.hasActiveTransaction() + + override fun notifyMessageSent(): Boolean = delegate.notifyMessageSent() + + override fun notifyOperationInitiated(operation: Any): Unit = delegate.notifyOperationInitiated(operation) + + override fun getTransactionOptions(): TransactionOptions = delegate.transactionOptions + + override fun startTransaction(): Unit = delegate.startTransaction() + + override fun startTransaction(transactionOptions: TransactionOptions): Unit = + delegate.startTransaction(transactionOptions) + + override fun commitTransaction(): Unit = delegate.commitTransaction() + + override fun abortTransaction(): Unit = delegate.abortTransaction() + + override fun withTransaction(transactionBody: TransactionBody): T = + throw UnsupportedOperationException() + + override fun withTransaction(transactionBody: TransactionBody, options: TransactionOptions): T = + throw UnsupportedOperationException() +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncDistinctIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncDistinctIterable.kt new file mode 100644 index 00000000000..ef580954e20 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncDistinctIterable.kt @@ -0,0 +1,35 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.DistinctIterable as JDistinctIterable +import com.mongodb.client.model.Collation +import com.mongodb.kotlin.client.DistinctIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +internal class SyncDistinctIterable(val wrapped: DistinctIterable) : + JDistinctIterable, SyncMongoIterable(wrapped) { + override fun batchSize(batchSize: Int): SyncDistinctIterable = apply { wrapped.batchSize(batchSize) } + override fun filter(filter: Bson?): SyncDistinctIterable = apply { wrapped.filter(filter) } + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncDistinctIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + override fun collation(collation: Collation?): SyncDistinctIterable = apply { wrapped.collation(collation) } + override fun comment(comment: String?): SyncDistinctIterable = apply { wrapped.comment(comment) } + override fun comment(comment: BsonValue?): SyncDistinctIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncFindIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncFindIterable.kt new file mode 100644 index 00000000000..c52866f1243 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncFindIterable.kt @@ -0,0 +1,92 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.CursorType +import com.mongodb.ExplainVerbosity +import com.mongodb.client.FindIterable as JFindIterable +import com.mongodb.client.model.Collation +import com.mongodb.kotlin.client.FindIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.Document +import org.bson.conversions.Bson + +@Suppress("DEPRECATION") +internal class SyncFindIterable(val wrapped: FindIterable) : + JFindIterable, SyncMongoIterable(wrapped) { + override fun batchSize(batchSize: Int): SyncFindIterable = apply { wrapped.batchSize(batchSize) } + override fun filter(filter: Bson?): SyncFindIterable = apply { wrapped.filter(filter) } + + override fun limit(limit: Int): SyncFindIterable = apply { wrapped.limit(limit) } + + override fun skip(skip: Int): SyncFindIterable = apply { wrapped.skip(skip) } + + override fun allowDiskUse(allowDiskUse: Boolean?): SyncFindIterable = apply { + wrapped.allowDiskUse(allowDiskUse) + } + + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncFindIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + override fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit): SyncFindIterable = apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + + override fun projection(projection: Bson?): SyncFindIterable = apply { wrapped.projection(projection) } + + override fun sort(sort: Bson?): SyncFindIterable = apply { wrapped.sort(sort) } + + override fun noCursorTimeout(noCursorTimeout: Boolean): SyncFindIterable = apply { + wrapped.noCursorTimeout(noCursorTimeout) + } + + @Suppress("OVERRIDE_DEPRECATION") + override fun oplogReplay(oplogReplay: Boolean): SyncFindIterable = apply { wrapped.oplogReplay(oplogReplay) } + + override fun partial(partial: Boolean): SyncFindIterable = apply { wrapped.partial(partial) } + + override fun cursorType(cursorType: CursorType): SyncFindIterable = apply { wrapped.cursorType(cursorType) } + + override fun collation(collation: Collation?): SyncFindIterable = apply { wrapped.collation(collation) } + + override fun comment(comment: String?): SyncFindIterable = apply { wrapped.comment(comment) } + + override fun comment(comment: BsonValue?): SyncFindIterable = apply { wrapped.comment(comment) } + + override fun hint(hint: Bson?): SyncFindIterable = apply { wrapped.hint(hint) } + + override fun hintString(hint: String?): SyncFindIterable = apply { wrapped.hintString(hint) } + + override fun let(variables: Bson?): SyncFindIterable = apply { wrapped.let(variables) } + override fun max(max: Bson?): SyncFindIterable = apply { wrapped.max(max) } + + override fun min(min: Bson?): SyncFindIterable = apply { wrapped.min(min) } + + override fun returnKey(returnKey: Boolean): SyncFindIterable = apply { wrapped.returnKey(returnKey) } + + override fun showRecordId(showRecordId: Boolean): SyncFindIterable = apply { wrapped.showRecordId(showRecordId) } + + override fun explain(): Document = wrapped.explain() + + override fun explain(verbosity: ExplainVerbosity): Document = wrapped.explain(verbosity) + + override fun explain(explainResultClass: Class): E = wrapped.explain(explainResultClass) + + override fun explain(explainResultClass: Class, verbosity: ExplainVerbosity): E = + wrapped.explain(explainResultClass, verbosity) +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListCollectionsIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListCollectionsIterable.kt new file mode 100644 index 00000000000..74579b15a20 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListCollectionsIterable.kt @@ -0,0 +1,36 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.ListCollectionsIterable as JListCollectionsIterable +import com.mongodb.kotlin.client.ListCollectionsIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +internal class SyncListCollectionsIterable(val wrapped: ListCollectionsIterable) : + JListCollectionsIterable, SyncMongoIterable(wrapped) { + + override fun batchSize(batchSize: Int): SyncListCollectionsIterable = apply { wrapped.batchSize(batchSize) } + + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncListCollectionsIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + override fun filter(filter: Bson?): SyncListCollectionsIterable = apply { wrapped.filter(filter) } + override fun comment(comment: String?): SyncListCollectionsIterable = apply { wrapped.comment(comment) } + override fun comment(comment: BsonValue?): SyncListCollectionsIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListDatabasesIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListDatabasesIterable.kt new file mode 100644 index 00000000000..2e0e662a65d --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListDatabasesIterable.kt @@ -0,0 +1,44 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.ListDatabasesIterable as JListDatabasesIterable +import com.mongodb.kotlin.client.ListDatabasesIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +internal class SyncListDatabasesIterable(val wrapped: ListDatabasesIterable) : + JListDatabasesIterable, SyncMongoIterable(wrapped) { + + override fun batchSize(batchSize: Int): SyncListDatabasesIterable = apply { wrapped.batchSize(batchSize) } + + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncListDatabasesIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + override fun filter(filter: Bson?): SyncListDatabasesIterable = apply { wrapped.filter(filter) } + + override fun nameOnly(nameOnly: Boolean?): SyncListDatabasesIterable = apply { wrapped.nameOnly(nameOnly) } + + override fun authorizedDatabasesOnly(authorizedDatabasesOnly: Boolean?): SyncListDatabasesIterable = apply { + wrapped.authorizedDatabasesOnly(authorizedDatabasesOnly) + } + + override fun comment(comment: String?): SyncListDatabasesIterable = apply { wrapped.comment(comment) } + + override fun comment(comment: BsonValue?): SyncListDatabasesIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListIndexesIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListIndexesIterable.kt new file mode 100644 index 00000000000..b9133970cb3 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncListIndexesIterable.kt @@ -0,0 +1,31 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.ListIndexesIterable as JListIndexesIterable +import com.mongodb.kotlin.client.ListIndexesIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue + +internal class SyncListIndexesIterable(val wrapped: ListIndexesIterable) : + JListIndexesIterable, SyncMongoIterable(wrapped) { + override fun batchSize(batchSize: Int): SyncListIndexesIterable = apply { wrapped.batchSize(batchSize) } + override fun maxTime(maxTime: Long, timeUnit: TimeUnit): SyncListIndexesIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + override fun comment(comment: String?): SyncListIndexesIterable = apply { wrapped.comment(comment) } + override fun comment(comment: BsonValue?): SyncListIndexesIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoChangeStreamCursor.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoChangeStreamCursor.kt new file mode 100644 index 00000000000..1ddfe72a866 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoChangeStreamCursor.kt @@ -0,0 +1,25 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.client.MongoChangeStreamCursor as JMongoChangeStreamCursor +import com.mongodb.kotlin.client.MongoChangeStreamCursor +import org.bson.BsonDocument + +internal class SyncMongoChangeStreamCursor(val wrapped: MongoChangeStreamCursor) : + JMongoChangeStreamCursor, SyncMongoCursor(wrapped) { + override fun getResumeToken(): BsonDocument? = wrapped.resumeToken +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt new file mode 100644 index 00000000000..9c3af8af290 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoClient.kt @@ -0,0 +1,89 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.ClientSessionOptions +import com.mongodb.client.ChangeStreamIterable +import com.mongodb.client.ClientSession +import com.mongodb.client.ListDatabasesIterable +import com.mongodb.client.MongoClient as JMongoClient +import com.mongodb.client.MongoDatabase +import com.mongodb.client.MongoIterable +import com.mongodb.connection.ClusterDescription +import com.mongodb.kotlin.client.MongoClient +import org.bson.Document +import org.bson.conversions.Bson + +internal class SyncMongoClient(val wrapped: MongoClient) : JMongoClient { + override fun close(): Unit = wrapped.close() + + override fun getDatabase(databaseName: String): MongoDatabase = SyncMongoDatabase(wrapped.getDatabase(databaseName)) + + override fun startSession(): ClientSession = SyncClientSession(wrapped.startSession(), this) + + override fun startSession(options: ClientSessionOptions): ClientSession = + SyncClientSession(wrapped.startSession(options), this) + + override fun listDatabaseNames(): MongoIterable = SyncMongoIterable(wrapped.listDatabaseNames()) + + override fun listDatabaseNames(clientSession: ClientSession): MongoIterable = + SyncMongoIterable(wrapped.listDatabaseNames(clientSession.unwrapped())) + + override fun listDatabases(): ListDatabasesIterable = SyncListDatabasesIterable(wrapped.listDatabases()) + + override fun listDatabases(clientSession: ClientSession): ListDatabasesIterable = + SyncListDatabasesIterable(wrapped.listDatabases(clientSession.unwrapped())) + + override fun listDatabases(resultClass: Class): ListDatabasesIterable = + SyncListDatabasesIterable(wrapped.listDatabases(resultClass)) + + override fun listDatabases( + clientSession: ClientSession, + resultClass: Class + ): ListDatabasesIterable = + SyncListDatabasesIterable(wrapped.listDatabases(clientSession.unwrapped(), resultClass)) + + override fun watch(): ChangeStreamIterable = SyncChangeStreamIterable(wrapped.watch()) + + override fun watch(resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(resultClass = resultClass)) + + override fun watch(pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline)) + + override fun watch(pipeline: MutableList, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + override fun watch(clientSession: ClientSession): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped())) + + override fun watch(clientSession: ClientSession, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), resultClass = resultClass)) + + override fun watch(clientSession: ClientSession, pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline)) + + override fun watch( + clientSession: ClientSession, + pipeline: MutableList, + resultClass: Class + ): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline, resultClass)) + + override fun getClusterDescription(): ClusterDescription = wrapped.clusterDescription + + private fun ClientSession.unwrapped() = (this as SyncClientSession).wrapped +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCollection.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCollection.kt new file mode 100644 index 00000000000..23a18872afc --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCollection.kt @@ -0,0 +1,505 @@ +/* + * 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. + */ +@file:Suppress("DEPRECATION") + +package com.mongodb.kotlin.client.syncadapter + +import com.mongodb.MongoNamespace +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.bulk.BulkWriteResult +import com.mongodb.client.AggregateIterable +import com.mongodb.client.ChangeStreamIterable +import com.mongodb.client.ClientSession +import com.mongodb.client.DistinctIterable +import com.mongodb.client.FindIterable +import com.mongodb.client.ListIndexesIterable +import com.mongodb.client.MapReduceIterable +import com.mongodb.client.MongoCollection as JMongoCollection +import com.mongodb.client.model.BulkWriteOptions +import com.mongodb.client.model.CountOptions +import com.mongodb.client.model.CreateIndexOptions +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 +import com.mongodb.client.model.FindOneAndReplaceOptions +import com.mongodb.client.model.FindOneAndUpdateOptions +import com.mongodb.client.model.IndexModel +import com.mongodb.client.model.IndexOptions +import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.InsertOneOptions +import com.mongodb.client.model.RenameCollectionOptions +import com.mongodb.client.model.ReplaceOptions +import com.mongodb.client.model.UpdateOptions +import com.mongodb.client.model.WriteModel +import com.mongodb.client.result.DeleteResult +import com.mongodb.client.result.InsertManyResult +import com.mongodb.client.result.InsertOneResult +import com.mongodb.client.result.UpdateResult +import com.mongodb.kotlin.client.MongoCollection +import java.lang.UnsupportedOperationException +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.bson.conversions.Bson + +@Suppress("OVERRIDE_DEPRECATION") +internal class SyncMongoCollection(val wrapped: MongoCollection) : JMongoCollection { + override fun getNamespace(): MongoNamespace = wrapped.namespace + + override fun getDocumentClass(): Class = wrapped.documentClass + + override fun getCodecRegistry(): CodecRegistry = wrapped.codecRegistry + + override fun getReadPreference(): ReadPreference = wrapped.readPreference + + override fun getWriteConcern(): WriteConcern = wrapped.writeConcern + + override fun getReadConcern(): ReadConcern = wrapped.readConcern + + override fun withDocumentClass(clazz: Class): SyncMongoCollection = + SyncMongoCollection(wrapped.withDocumentClass(clazz)) + + override fun withCodecRegistry(codecRegistry: CodecRegistry): SyncMongoCollection = + SyncMongoCollection(wrapped.withCodecRegistry(codecRegistry)) + + override fun withReadPreference(readPreference: ReadPreference): SyncMongoCollection = + SyncMongoCollection(wrapped.withReadPreference(readPreference)) + + override fun withWriteConcern(writeConcern: WriteConcern): SyncMongoCollection = + SyncMongoCollection(wrapped.withWriteConcern(writeConcern)) + + override fun withReadConcern(readConcern: ReadConcern): SyncMongoCollection = + SyncMongoCollection(wrapped.withReadConcern(readConcern)) + + override fun countDocuments(): Long = wrapped.countDocuments() + + override fun countDocuments(filter: Bson): Long = wrapped.countDocuments(filter) + + override fun countDocuments(filter: Bson, options: CountOptions): Long = wrapped.countDocuments(filter, options) + + override fun countDocuments(clientSession: ClientSession): Long = wrapped.countDocuments(clientSession.unwrapped()) + + override fun countDocuments(clientSession: ClientSession, filter: Bson): Long = + wrapped.countDocuments(clientSession.unwrapped(), filter) + + override fun countDocuments(clientSession: ClientSession, filter: Bson, options: CountOptions): Long = + wrapped.countDocuments(clientSession.unwrapped(), filter, options) + + override fun estimatedDocumentCount(): Long = wrapped.estimatedDocumentCount() + + override fun estimatedDocumentCount(options: EstimatedDocumentCountOptions): Long = + wrapped.estimatedDocumentCount(options) + + override fun distinct(fieldName: String, resultClass: Class): DistinctIterable = + SyncDistinctIterable(wrapped.distinct(fieldName, resultClass = resultClass)) + + override fun distinct(fieldName: String, filter: Bson, resultClass: Class): DistinctIterable = + SyncDistinctIterable(wrapped.distinct(fieldName, filter, resultClass = resultClass)) + + override fun distinct( + clientSession: ClientSession, + fieldName: String, + resultClass: Class + ): DistinctIterable = + SyncDistinctIterable(wrapped.distinct(clientSession.unwrapped(), fieldName, resultClass = resultClass)) + + override fun distinct( + clientSession: ClientSession, + fieldName: String, + filter: Bson, + resultClass: Class + ): DistinctIterable = + SyncDistinctIterable(wrapped.distinct(clientSession.unwrapped(), fieldName, filter, resultClass)) + + override fun find(): FindIterable = SyncFindIterable(wrapped.find()) + + override fun find(resultClass: Class): FindIterable = + SyncFindIterable(wrapped.find(resultClass = resultClass)) + + override fun find(filter: Bson): FindIterable = SyncFindIterable(wrapped.find(filter)) + + override fun find(filter: Bson, resultClass: Class): FindIterable = + SyncFindIterable(wrapped.find(filter, resultClass)) + + override fun find(clientSession: ClientSession): FindIterable = + SyncFindIterable(wrapped.find(clientSession.unwrapped())) + + override fun find(clientSession: ClientSession, resultClass: Class): FindIterable = + SyncFindIterable(wrapped.find(clientSession.unwrapped(), resultClass = resultClass)) + + override fun find(clientSession: ClientSession, filter: Bson): FindIterable = + SyncFindIterable(wrapped.find(clientSession.unwrapped(), filter)) + + override fun find(clientSession: ClientSession, filter: Bson, resultClass: Class): FindIterable = + SyncFindIterable(wrapped.find(clientSession.unwrapped(), filter, resultClass)) + + override fun aggregate(pipeline: MutableList): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(pipeline)) + + override fun aggregate(pipeline: MutableList, resultClass: Class): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(pipeline, resultClass)) + + override fun aggregate(clientSession: ClientSession, pipeline: MutableList): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(clientSession.unwrapped(), pipeline)) + + override fun aggregate( + clientSession: ClientSession, + pipeline: MutableList, + resultClass: Class + ): AggregateIterable = SyncAggregateIterable(wrapped.aggregate(clientSession.unwrapped(), pipeline, resultClass)) + + override fun watch(): ChangeStreamIterable = SyncChangeStreamIterable(wrapped.watch()) + + override fun watch(resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(resultClass = resultClass)) + + override fun watch(pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline)) + + override fun watch(pipeline: MutableList, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + override fun watch(clientSession: ClientSession): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped())) + + override fun watch(clientSession: ClientSession, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), resultClass = resultClass)) + + override fun watch(clientSession: ClientSession, pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline)) + + override fun watch( + clientSession: ClientSession, + pipeline: MutableList, + resultClass: Class + ): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline, resultClass)) + + override fun mapReduce(mapFunction: String, reduceFunction: String): MapReduceIterable = + throw UnsupportedOperationException("No MapReduce support") + + override fun mapReduce( + mapFunction: String, + reduceFunction: String, + resultClass: Class + ): MapReduceIterable = throw UnsupportedOperationException("No MapReduce support") + + override fun mapReduce( + clientSession: ClientSession, + mapFunction: String, + reduceFunction: String + ): MapReduceIterable = throw UnsupportedOperationException("No MapReduce support") + + override fun mapReduce( + clientSession: ClientSession, + mapFunction: String, + reduceFunction: String, + resultClass: Class + ): MapReduceIterable = throw UnsupportedOperationException("No MapReduce support") + + override fun deleteOne(filter: Bson): DeleteResult = wrapped.deleteOne(filter) + + override fun deleteOne(filter: Bson, options: DeleteOptions): DeleteResult = wrapped.deleteOne(filter, options) + + override fun deleteOne(clientSession: ClientSession, filter: Bson): DeleteResult = + wrapped.deleteOne(clientSession.unwrapped(), filter) + + override fun deleteOne(clientSession: ClientSession, filter: Bson, options: DeleteOptions): DeleteResult = + wrapped.deleteOne(clientSession.unwrapped(), filter, options) + + override fun deleteMany(filter: Bson): DeleteResult = wrapped.deleteMany(filter) + + override fun deleteMany(filter: Bson, options: DeleteOptions): DeleteResult = wrapped.deleteMany(filter, options) + + override fun deleteMany(clientSession: ClientSession, filter: Bson): DeleteResult = + wrapped.deleteMany(clientSession.unwrapped(), filter) + + override fun deleteMany(clientSession: ClientSession, filter: Bson, options: DeleteOptions): DeleteResult = + wrapped.deleteMany(clientSession.unwrapped(), filter, options) + + override fun updateOne(filter: Bson, update: Bson): UpdateResult = wrapped.updateOne(filter, update) + + override fun updateOne(filter: Bson, update: Bson, updateOptions: UpdateOptions): UpdateResult = + wrapped.updateOne(filter, update, updateOptions) + + override fun updateOne(clientSession: ClientSession, filter: Bson, update: Bson): UpdateResult = + wrapped.updateOne(clientSession.unwrapped(), filter, update) + + override fun updateOne( + clientSession: ClientSession, + filter: Bson, + update: Bson, + updateOptions: UpdateOptions + ): UpdateResult = wrapped.updateOne(clientSession.unwrapped(), filter, update, updateOptions) + + override fun updateOne(filter: Bson, update: MutableList): UpdateResult = + wrapped.updateOne(filter, update) + + override fun updateOne(filter: Bson, update: MutableList, updateOptions: UpdateOptions): UpdateResult = + wrapped.updateOne(filter, update, updateOptions) + + override fun updateOne(clientSession: ClientSession, filter: Bson, update: MutableList): UpdateResult = + wrapped.updateOne(clientSession.unwrapped(), filter, update) + + override fun updateOne( + clientSession: ClientSession, + filter: Bson, + update: MutableList, + updateOptions: UpdateOptions + ): UpdateResult = wrapped.updateOne(clientSession.unwrapped(), filter, update, updateOptions) + + override fun updateMany(filter: Bson, update: Bson): UpdateResult = wrapped.updateMany(filter, update) + + override fun updateMany(filter: Bson, update: Bson, updateOptions: UpdateOptions): UpdateResult = + wrapped.updateMany(filter, update, updateOptions) + + override fun updateMany(clientSession: ClientSession, filter: Bson, update: Bson): UpdateResult = + wrapped.updateMany(clientSession.unwrapped(), filter, update) + + override fun updateMany( + clientSession: ClientSession, + filter: Bson, + update: Bson, + updateOptions: UpdateOptions + ): UpdateResult = wrapped.updateMany(clientSession.unwrapped(), filter, update, updateOptions) + + override fun updateMany(filter: Bson, update: MutableList): UpdateResult = + wrapped.updateMany(filter, update) + + override fun updateMany(filter: Bson, update: MutableList, updateOptions: UpdateOptions): UpdateResult = + wrapped.updateMany(filter, update, updateOptions) + + override fun updateMany(clientSession: ClientSession, filter: Bson, update: MutableList): UpdateResult = + wrapped.updateMany(clientSession.unwrapped(), filter, update) + + override fun updateMany( + clientSession: ClientSession, + filter: Bson, + update: MutableList, + updateOptions: UpdateOptions + ): UpdateResult = wrapped.updateMany(clientSession.unwrapped(), filter, update, updateOptions) + + override fun findOneAndDelete(filter: Bson): T? = wrapped.findOneAndDelete(filter) + + override fun findOneAndDelete(filter: Bson, options: FindOneAndDeleteOptions): T? = + wrapped.findOneAndDelete(filter, options) + + override fun findOneAndDelete(clientSession: ClientSession, filter: Bson): T? = + wrapped.findOneAndDelete(clientSession.unwrapped(), filter) + + override fun findOneAndDelete(clientSession: ClientSession, filter: Bson, options: FindOneAndDeleteOptions): T? = + wrapped.findOneAndDelete(clientSession.unwrapped(), filter, options) + + override fun findOneAndUpdate(filter: Bson, update: Bson): T? = wrapped.findOneAndUpdate(filter, update) + + override fun findOneAndUpdate(filter: Bson, update: Bson, options: FindOneAndUpdateOptions): T? = + wrapped.findOneAndUpdate(filter, update, options) + + override fun findOneAndUpdate(clientSession: ClientSession, filter: Bson, update: Bson): T? = + wrapped.findOneAndUpdate(clientSession.unwrapped(), filter, update) + + override fun findOneAndUpdate( + clientSession: ClientSession, + filter: Bson, + update: Bson, + options: FindOneAndUpdateOptions + ): T? = wrapped.findOneAndUpdate(clientSession.unwrapped(), filter, update, options) + + override fun findOneAndUpdate(filter: Bson, update: MutableList): T? = + wrapped.findOneAndUpdate(filter, update) + + override fun findOneAndUpdate(filter: Bson, update: MutableList, options: FindOneAndUpdateOptions): T? = + wrapped.findOneAndUpdate(filter, update, options) + + override fun findOneAndUpdate(clientSession: ClientSession, filter: Bson, update: MutableList): T? = + wrapped.findOneAndUpdate(clientSession.unwrapped(), filter, update) + + override fun findOneAndUpdate( + clientSession: ClientSession, + filter: Bson, + update: MutableList, + options: FindOneAndUpdateOptions + ): T? = wrapped.findOneAndUpdate(clientSession.unwrapped(), filter, update, options) + + override fun drop() = wrapped.drop() + + override fun drop(clientSession: ClientSession) = wrapped.drop(clientSession.unwrapped()) + + override fun drop(dropCollectionOptions: DropCollectionOptions) = wrapped.drop(dropCollectionOptions) + + override fun drop(clientSession: ClientSession, dropCollectionOptions: DropCollectionOptions) = + wrapped.drop(clientSession.unwrapped(), dropCollectionOptions) + + override fun createIndex(keys: Bson): String = wrapped.createIndex(keys) + + override fun createIndex(keys: Bson, indexOptions: IndexOptions): String = wrapped.createIndex(keys, indexOptions) + + override fun createIndex(clientSession: ClientSession, keys: Bson): String = + wrapped.createIndex(clientSession.unwrapped(), keys) + + override fun createIndex(clientSession: ClientSession, keys: Bson, indexOptions: IndexOptions): String = + wrapped.createIndex(clientSession.unwrapped(), keys, indexOptions) + + override fun createIndexes(indexes: MutableList): MutableList = + wrapped.createIndexes(indexes).toMutableList() + + override fun createIndexes( + indexes: MutableList, + createIndexOptions: CreateIndexOptions + ): MutableList = wrapped.createIndexes(indexes, createIndexOptions).toMutableList() + + override fun createIndexes(clientSession: ClientSession, indexes: MutableList): MutableList = + wrapped.createIndexes(clientSession.unwrapped(), indexes).toMutableList() + + override fun createIndexes( + clientSession: ClientSession, + indexes: MutableList, + createIndexOptions: CreateIndexOptions + ): MutableList = + wrapped.createIndexes(clientSession.unwrapped(), indexes, createIndexOptions).toMutableList() + + override fun listIndexes(): ListIndexesIterable = SyncListIndexesIterable(wrapped.listIndexes()) + + override fun listIndexes(resultClass: Class): ListIndexesIterable = + SyncListIndexesIterable(wrapped.listIndexes(resultClass = resultClass)) + + override fun listIndexes(clientSession: ClientSession): ListIndexesIterable = + SyncListIndexesIterable(wrapped.listIndexes(clientSession.unwrapped())) + + override fun listIndexes(clientSession: ClientSession, resultClass: Class): ListIndexesIterable = + SyncListIndexesIterable(wrapped.listIndexes(clientSession.unwrapped(), resultClass)) + + override fun dropIndex(indexName: String) = wrapped.dropIndex(indexName) + + override fun dropIndex(indexName: String, dropIndexOptions: DropIndexOptions) = + wrapped.dropIndex(indexName, dropIndexOptions) + + override fun dropIndex(keys: Bson) = wrapped.dropIndex(keys) + + override fun dropIndex(keys: Bson, dropIndexOptions: DropIndexOptions) = wrapped.dropIndex(keys, dropIndexOptions) + + override fun dropIndex(clientSession: ClientSession, indexName: String) = + wrapped.dropIndex(clientSession.unwrapped(), indexName) + + override fun dropIndex(clientSession: ClientSession, keys: Bson) = + wrapped.dropIndex(clientSession.unwrapped(), keys) + override fun dropIndex(clientSession: ClientSession, indexName: String, dropIndexOptions: DropIndexOptions) = + wrapped.dropIndex(clientSession.unwrapped(), indexName, dropIndexOptions) + + override fun dropIndex(clientSession: ClientSession, keys: Bson, dropIndexOptions: DropIndexOptions) = + wrapped.dropIndex(clientSession.unwrapped(), keys, dropIndexOptions) + + override fun dropIndexes() = wrapped.dropIndexes() + + override fun dropIndexes(clientSession: ClientSession) = wrapped.dropIndexes(clientSession.unwrapped()) + + override fun dropIndexes(dropIndexOptions: DropIndexOptions) = wrapped.dropIndexes(dropIndexOptions) + + override fun dropIndexes(clientSession: ClientSession, dropIndexOptions: DropIndexOptions) = + wrapped.dropIndexes(clientSession.unwrapped(), dropIndexOptions) + + override fun renameCollection(newCollectionNamespace: MongoNamespace) = + wrapped.renameCollection(newCollectionNamespace) + + override fun renameCollection( + newCollectionNamespace: MongoNamespace, + renameCollectionOptions: RenameCollectionOptions + ) = wrapped.renameCollection(newCollectionNamespace, renameCollectionOptions) + + override fun renameCollection(clientSession: ClientSession, newCollectionNamespace: MongoNamespace) = + wrapped.renameCollection(clientSession.unwrapped(), newCollectionNamespace) + + override fun renameCollection( + clientSession: ClientSession, + newCollectionNamespace: MongoNamespace, + renameCollectionOptions: RenameCollectionOptions + ) = wrapped.renameCollection(clientSession.unwrapped(), newCollectionNamespace, renameCollectionOptions) + override fun findOneAndReplace( + clientSession: ClientSession, + filter: Bson, + replacement: T, + options: FindOneAndReplaceOptions + ): T? = wrapped.findOneAndReplace(clientSession.unwrapped(), filter, replacement, options) + + override fun findOneAndReplace(clientSession: ClientSession, filter: Bson, replacement: T): T? = + wrapped.findOneAndReplace(clientSession.unwrapped(), filter, replacement) + + override fun findOneAndReplace(filter: Bson, replacement: T, options: FindOneAndReplaceOptions): T? = + wrapped.findOneAndReplace(filter, replacement, options) + + override fun findOneAndReplace(filter: Bson, replacement: T): T? = wrapped.findOneAndReplace(filter, replacement) + + override fun replaceOne( + clientSession: ClientSession, + filter: Bson, + replacement: T, + replaceOptions: ReplaceOptions + ): UpdateResult = wrapped.replaceOne(clientSession.unwrapped(), filter, replacement, replaceOptions) + + override fun replaceOne(clientSession: ClientSession, filter: Bson, replacement: T): UpdateResult = + wrapped.replaceOne(clientSession.unwrapped(), filter, replacement) + + override fun replaceOne(filter: Bson, replacement: T, replaceOptions: ReplaceOptions): UpdateResult = + wrapped.replaceOne(filter, replacement, replaceOptions) + + override fun replaceOne(filter: Bson, replacement: T): UpdateResult = wrapped.replaceOne(filter, replacement) + + override fun insertMany( + clientSession: ClientSession, + documents: MutableList, + options: InsertManyOptions + ): InsertManyResult = wrapped.insertMany(clientSession.unwrapped(), documents, options) + + override fun insertMany(clientSession: ClientSession, documents: MutableList): InsertManyResult = + wrapped.insertMany(clientSession.unwrapped(), documents) + + override fun insertMany(documents: MutableList, options: InsertManyOptions): InsertManyResult = + wrapped.insertMany(documents, options) + + override fun insertMany(documents: MutableList): InsertManyResult = wrapped.insertMany(documents) + + override fun insertOne(clientSession: ClientSession, document: T, options: InsertOneOptions): InsertOneResult = + wrapped.insertOne(clientSession.unwrapped(), document, options) + + override fun insertOne(clientSession: ClientSession, document: T): InsertOneResult = + wrapped.insertOne(clientSession.unwrapped(), document) + + override fun insertOne(document: T, options: InsertOneOptions): InsertOneResult = + wrapped.insertOne(document, options) + + override fun insertOne(document: T): InsertOneResult = wrapped.insertOne(document) + + override fun bulkWrite( + clientSession: ClientSession, + requests: MutableList>, + options: BulkWriteOptions + ): BulkWriteResult = wrapped.bulkWrite(clientSession.unwrapped(), requests, options) + + override fun bulkWrite( + clientSession: ClientSession, + requests: MutableList> + ): BulkWriteResult = wrapped.bulkWrite(clientSession.unwrapped(), requests) + + override fun bulkWrite(requests: MutableList>, options: BulkWriteOptions): BulkWriteResult = + wrapped.bulkWrite(requests, options) + + override fun bulkWrite(requests: MutableList>): BulkWriteResult = wrapped.bulkWrite(requests) + + private fun ClientSession.unwrapped() = (this as SyncClientSession).wrapped +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCursor.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCursor.kt new file mode 100644 index 00000000000..d115e979521 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoCursor.kt @@ -0,0 +1,40 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.ServerAddress +import com.mongodb.ServerCursor +import com.mongodb.client.MongoCursor as JMongoCursor +import com.mongodb.kotlin.client.MongoCursor + +internal open class SyncMongoCursor(private val delegate: MongoCursor) : JMongoCursor { + override fun remove() { + TODO("Not yet implemented") + } + + override fun hasNext(): Boolean = delegate.hasNext() + override fun next(): T = delegate.next() + + override fun close() = delegate.close() + + override fun available(): Int = delegate.available + + override fun tryNext(): T? = delegate.tryNext() + + override fun getServerCursor(): ServerCursor? = delegate.serverCursor + + override fun getServerAddress(): ServerAddress = delegate.serverAddress +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoDatabase.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoDatabase.kt new file mode 100644 index 00000000000..20b0051488f --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoDatabase.kt @@ -0,0 +1,197 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.client.AggregateIterable +import com.mongodb.client.ChangeStreamIterable +import com.mongodb.client.ClientSession +import com.mongodb.client.ListCollectionsIterable +import com.mongodb.client.MongoCollection +import com.mongodb.client.MongoDatabase as JMongoDatabase +import com.mongodb.client.MongoIterable +import com.mongodb.client.model.CreateCollectionOptions +import com.mongodb.client.model.CreateViewOptions +import com.mongodb.kotlin.client.MongoDatabase +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.bson.conversions.Bson + +internal class SyncMongoDatabase(val wrapped: MongoDatabase) : JMongoDatabase { + override fun getName(): String = wrapped.name + + override fun getCodecRegistry(): CodecRegistry = wrapped.codecRegistry + + override fun getReadPreference(): ReadPreference = wrapped.readPreference + + override fun getWriteConcern(): WriteConcern = wrapped.writeConcern + + override fun getReadConcern(): ReadConcern = wrapped.readConcern + + override fun withCodecRegistry(codecRegistry: CodecRegistry): SyncMongoDatabase = + SyncMongoDatabase(wrapped.withCodecRegistry(codecRegistry)) + + override fun withReadPreference(readPreference: ReadPreference): SyncMongoDatabase = + SyncMongoDatabase(wrapped.withReadPreference(readPreference)) + + override fun withWriteConcern(writeConcern: WriteConcern): SyncMongoDatabase = + SyncMongoDatabase(wrapped.withWriteConcern(writeConcern)) + + override fun withReadConcern(readConcern: ReadConcern): SyncMongoDatabase = + SyncMongoDatabase(wrapped.withReadConcern(readConcern)) + + override fun getCollection(collectionName: String): MongoCollection = + SyncMongoCollection(wrapped.getCollection(collectionName, Document::class.java)) + + override fun getCollection(collectionName: String, documentClass: Class): MongoCollection = + SyncMongoCollection(wrapped.getCollection(collectionName, documentClass)) + + override fun runCommand(command: Bson): Document = wrapped.runCommand(command) + + override fun runCommand(command: Bson, readPreference: ReadPreference): Document = + wrapped.runCommand(command, readPreference) + + override fun runCommand(command: Bson, resultClass: Class): T = + wrapped.runCommand(command, resultClass = resultClass) + + override fun runCommand(command: Bson, readPreference: ReadPreference, resultClass: Class): T = + wrapped.runCommand(command, readPreference, resultClass) + + override fun runCommand(clientSession: ClientSession, command: Bson): Document = + wrapped.runCommand(clientSession.unwrapped(), command) + + override fun runCommand(clientSession: ClientSession, command: Bson, readPreference: ReadPreference): Document = + wrapped.runCommand(clientSession.unwrapped(), command, readPreference) + + override fun runCommand(clientSession: ClientSession, command: Bson, resultClass: Class): T = + wrapped.runCommand(clientSession.unwrapped(), command, resultClass = resultClass) + + override fun runCommand( + clientSession: ClientSession, + command: Bson, + readPreference: ReadPreference, + resultClass: Class + ): T = wrapped.runCommand(clientSession.unwrapped(), command, readPreference, resultClass) + + override fun drop() = wrapped.drop() + + override fun drop(clientSession: ClientSession) = wrapped.drop(clientSession.unwrapped()) + + override fun listCollectionNames(): MongoIterable = SyncMongoIterable(wrapped.listCollectionNames()) + + override fun listCollectionNames(clientSession: ClientSession): MongoIterable = + SyncMongoIterable(wrapped.listCollectionNames(clientSession.unwrapped())) + + override fun listCollections(): ListCollectionsIterable = + SyncListCollectionsIterable(wrapped.listCollections()) + + override fun listCollections(resultClass: Class): ListCollectionsIterable = + SyncListCollectionsIterable(wrapped.listCollections(resultClass)) + + override fun listCollections(clientSession: ClientSession): ListCollectionsIterable = + SyncListCollectionsIterable(wrapped.listCollections(clientSession.unwrapped())) + + override fun listCollections( + clientSession: ClientSession, + resultClass: Class + ): ListCollectionsIterable = + SyncListCollectionsIterable(wrapped.listCollections(clientSession.unwrapped(), resultClass)) + + override fun createCollection(collectionName: String) = wrapped.createCollection(collectionName) + + override fun createCollection(collectionName: String, createCollectionOptions: CreateCollectionOptions) = + wrapped.createCollection(collectionName, createCollectionOptions) + + override fun createCollection(clientSession: ClientSession, collectionName: String) = + wrapped.createCollection(clientSession.unwrapped(), collectionName) + + override fun createCollection( + clientSession: ClientSession, + collectionName: String, + createCollectionOptions: CreateCollectionOptions + ) = wrapped.createCollection(clientSession.unwrapped(), collectionName, createCollectionOptions) + + override fun createView(viewName: String, viewOn: String, pipeline: MutableList) = + wrapped.createView(viewName, viewOn, pipeline) + + override fun createView( + viewName: String, + viewOn: String, + pipeline: MutableList, + createViewOptions: CreateViewOptions + ) = wrapped.createView(viewName, viewOn, pipeline, createViewOptions) + + override fun createView( + clientSession: ClientSession, + viewName: String, + viewOn: String, + pipeline: MutableList + ) = wrapped.createView(clientSession.unwrapped(), viewName, viewOn, pipeline) + + override fun createView( + clientSession: ClientSession, + viewName: String, + viewOn: String, + pipeline: MutableList, + createViewOptions: CreateViewOptions + ) = wrapped.createView(clientSession.unwrapped(), viewName, viewOn, pipeline, createViewOptions) + + override fun watch(): ChangeStreamIterable = SyncChangeStreamIterable(wrapped.watch()) + + override fun watch(resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(resultClass = resultClass)) + + override fun watch(pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline)) + + override fun watch(pipeline: MutableList, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + override fun watch(clientSession: ClientSession): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped())) + + override fun watch(clientSession: ClientSession, resultClass: Class): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), resultClass = resultClass)) + + override fun watch(clientSession: ClientSession, pipeline: MutableList): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline)) + + override fun watch( + clientSession: ClientSession, + pipeline: MutableList, + resultClass: Class + ): ChangeStreamIterable = + SyncChangeStreamIterable(wrapped.watch(clientSession.unwrapped(), pipeline, resultClass)) + + override fun aggregate(pipeline: MutableList): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(pipeline)) + + override fun aggregate(pipeline: MutableList, resultClass: Class): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(pipeline, resultClass)) + + override fun aggregate(clientSession: ClientSession, pipeline: MutableList): AggregateIterable = + SyncAggregateIterable(wrapped.aggregate(clientSession.unwrapped(), pipeline)) + + override fun aggregate( + clientSession: ClientSession, + pipeline: MutableList, + resultClass: Class + ): AggregateIterable = SyncAggregateIterable(wrapped.aggregate(clientSession.unwrapped(), pipeline, resultClass)) + + private fun ClientSession.unwrapped() = (this as SyncClientSession).wrapped +} diff --git a/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoIterable.kt b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoIterable.kt new file mode 100644 index 00000000000..7a7f7af5221 --- /dev/null +++ b/driver-kotlin-sync/src/integration/kotlin/com/mongodb/kotlin/client/syncadapter/SyncMongoIterable.kt @@ -0,0 +1,40 @@ +/* + * 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 com.mongodb.kotlin.client.syncadapter + +import com.mongodb.Function +import com.mongodb.client.MongoCursor +import com.mongodb.client.MongoIterable as JMongoIterable +import com.mongodb.kotlin.client.MongoIterable + +internal open class SyncMongoIterable(val delegate: MongoIterable) : JMongoIterable { + override fun iterator(): MongoCursor = cursor() + + override fun cursor(): MongoCursor = SyncMongoCursor(delegate.cursor()) + + override fun first(): T? = delegate.firstOrNull() + + override fun batchSize(batchSize: Int): SyncMongoIterable = apply { delegate.batchSize(batchSize) } + + @Suppress("UNCHECKED_CAST") + override fun ?> into(target: A): A & Any { + delegate.forEach { target?.add(it) } + return target as (A & Any) + } + + override fun map(mapper: Function): SyncMongoIterable = + SyncMongoIterable(delegate.map { mapper.apply(it) }) +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/AggregateIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/AggregateIterable.kt new file mode 100644 index 00000000000..4940cad99d0 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/AggregateIterable.kt @@ -0,0 +1,191 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ExplainVerbosity +import com.mongodb.client.AggregateIterable as JAggregateIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.Document +import org.bson.conversions.Bson + +/** + * Iterable like implementation for aggregate operations. + * + * @param T The type of the result. + * @see [Aggregation command](https://www.mongodb.com/docs/manual/reference/command/aggregate) + */ +public class AggregateIterable(private val wrapped: JAggregateIterable) : MongoIterable(wrapped) { + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): AggregateIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Aggregates documents according to the specified aggregation pipeline, which must end with a $out or $merge stage. + * + * @throws IllegalStateException if the pipeline does not end with a $out or $merge stage + * @see [$out stage](https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/) + * @see [$merge stage](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/) + */ + public fun toCollection(): Unit = wrapped.toCollection() + + /** + * Enables writing to temporary files. A null value indicates that it's unspecified. + * + * @param allowDiskUse true if writing to temporary files is enabled + * @return this + * @see [Aggregation command](https://www.mongodb.com/docs/manual/reference/command/aggregate/) + */ + public fun allowDiskUse(allowDiskUse: Boolean?): AggregateIterable = apply { wrapped.allowDiskUse(allowDiskUse) } + + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, defaults to Milliseconds + * @return this + * @see [Max Time](https://www.mongodb.com/docs/manual/reference/method/cursor.maxTimeMS/#cursor.maxTimeMS) + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): AggregateIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * The maximum amount of time for the server to wait on new documents to satisfy a `$changeStream` aggregation. + * + * A zero value will be ignored. + * + * @param maxAwaitTime the max await time + * @param timeUnit the time unit to return the result in, defaults to Milliseconds + * @return the maximum await execution time in the given time unit + */ + public fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): AggregateIterable = + apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + + /** + * Sets the bypass document level validation flag. + * + * Note: This only applies when an $out or $merge stage is specified. + * + * @param bypassDocumentValidation If true, allows the write to opt-out of document level validation. + * @return this + * @see [Aggregation command](https://www.mongodb.com/docs/manual/reference/command/aggregate/) + */ + public fun bypassDocumentValidation(bypassDocumentValidation: Boolean?): AggregateIterable = apply { + wrapped.bypassDocumentValidation(bypassDocumentValidation) + } + + /** + * Sets the collation options + * + * A null value represents the server default. + * + * @param collation the collation options to use + * @return this + */ + public fun collation(collation: Collation?): AggregateIterable = apply { wrapped.collation(collation) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): AggregateIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * The comment can be any valid BSON type for server versions 4.4 and above. Server versions between 3.6 and 4.2 + * only support string as comment, and providing a non-string type will result in a server-side error. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): AggregateIterable = apply { wrapped.comment(comment) } + + /** + * Sets the hint for which index to use. A null value means no hint is set. + * + * @param hint the hint + * @return this + */ + public fun hint(hint: Bson?): AggregateIterable = apply { wrapped.hint(hint) } + + /** + * Sets the hint to apply. + * + * Note: If [AggregateIterable.hint] is set that will be used instead of any hint string. + * + * @param hint the name of the index which should be used for the operation + * @return this + */ + public fun hintString(hint: String?): AggregateIterable = apply { wrapped.hintString(hint) } + + /** + * Add top-level variables to the aggregation. + * + * For MongoDB 5.0+, the aggregate command accepts a `let` option. This option is a document consisting of zero or + * more fields representing variables that are accessible to the aggregation pipeline. The key is the name of the + * variable and the value is a constant in the aggregate expression language. Each parameter name is then usable to + * access the value of the corresponding expression with the "$$" syntax within aggregate expression contexts which + * may require the use of $expr or a pipeline. + * + * @param variables the variables + * @return this + */ + public fun let(variables: Bson?): AggregateIterable = apply { wrapped.let(variables) } + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public fun explain(verbosity: ExplainVerbosity? = null): Document = explain(verbosity) + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param R the type of the document class + * @param resultClass the result document type. + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public fun explain(resultClass: Class, verbosity: ExplainVerbosity? = null): R = + if (verbosity == null) wrapped.explain(resultClass) else wrapped.explain(resultClass, verbosity) + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param R the type of the document class + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public inline fun explain(verbosity: ExplainVerbosity? = null): R = + explain(R::class.java, verbosity) +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ChangeStreamIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ChangeStreamIterable.kt new file mode 100644 index 00000000000..95660682f0b --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ChangeStreamIterable.kt @@ -0,0 +1,185 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ChangeStreamIterable as JChangeStreamIterable +import com.mongodb.client.model.Collation +import com.mongodb.client.model.changestream.ChangeStreamDocument +import com.mongodb.client.model.changestream.FullDocument +import com.mongodb.client.model.changestream.FullDocumentBeforeChange +import java.util.concurrent.TimeUnit +import org.bson.BsonDocument +import org.bson.BsonTimestamp +import org.bson.BsonValue + +/** + * Iterable like implementation for change streams. + * + * Note: the [ChangeStreamDocument] class will not be applicable for all change stream outputs. If using custom + * pipelines that radically change the result, then the [withDocumentClass] method can be used to provide an alternative + * document format. + * + * @param T The type of the result. + */ +public class ChangeStreamIterable(private val wrapped: JChangeStreamIterable) : + MongoIterable>(wrapped) { + + /** + * Returns a cursor used for iterating over elements of type {@code ChangeStreamDocument}. The cursor has a + * covariant return type to additionally provide a method to access the resume token in change stream batches. + * + * @return the change stream cursor + */ + public override fun cursor(): MongoChangeStreamCursor> = + MongoChangeStreamCursorImpl(wrapped.cursor()) + + /** + * Sets the fullDocument value. + * + * @param fullDocument the fullDocument + * @return this + */ + public fun fullDocument(fullDocument: FullDocument): ChangeStreamIterable = apply { + wrapped.fullDocument(fullDocument) + } + + /** + * Sets the fullDocumentBeforeChange value. + * + * @param fullDocumentBeforeChange the fullDocumentBeforeChange + * @return this + */ + public fun fullDocumentBeforeChange(fullDocumentBeforeChange: FullDocumentBeforeChange): ChangeStreamIterable = + apply { + wrapped.fullDocumentBeforeChange(fullDocumentBeforeChange) + } + + /** + * Sets the logical starting point for the new change stream. + * + * @param resumeToken the resume token + * @return this + */ + public fun resumeAfter(resumeToken: BsonDocument): ChangeStreamIterable = apply { + wrapped.resumeAfter(resumeToken) + } + + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): ChangeStreamIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the maximum await execution time on the server for this operation. + * + * @param maxAwaitTime the max await time. A zero value will be ignored, and indicates that the driver should + * respect the server's default value + * @param timeUnit the time unit, which defaults to MILLISECONDS + * @return this + */ + public fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): ChangeStreamIterable = + apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + + /** + * Sets the collation options + * + * A null value represents the server default. + * + * @param collation the collation options to use + * @return this + */ + public fun collation(collation: Collation?): ChangeStreamIterable = apply { wrapped.collation(collation) } + + /** + * Returns a `MongoIterable` containing the results of the change stream based on the document class provided. + * + * @param R the Mongo Iterable type + * @param resultClass the target document type of the iterable. + * @return the new Mongo Iterable + */ + public fun withDocumentClass(resultClass: Class): MongoIterable = + MongoIterable(wrapped.withDocumentClass(resultClass)) + + /** + * Returns a `MongoIterable` containing the results of the change stream based on the document class provided. + * + * @param R the Mongo Iterable type + * @return the new Mongo Iterable + */ + public inline fun withDocumentClass(): MongoIterable = withDocumentClass(R::class.java) + + /** + * The change stream will only provide changes that occurred at or after the specified timestamp. + * + * Any command run against the server will return an operation time that can be used here. + * + * The default value is an operation time obtained from the server before the change stream was created. + * + * @param startAtOperationTime the start at operation time + * @return this + */ + public fun startAtOperationTime(startAtOperationTime: BsonTimestamp): ChangeStreamIterable = apply { + wrapped.startAtOperationTime(startAtOperationTime) + } + + /** + * Similar to `resumeAfter`, this option takes a resume token and starts a new change stream returning the first + * notification after the token. + * + * This will allow users to watch collections that have been dropped and recreated or newly renamed collections + * without missing any notifications. + * + * Note: The server will report an error if both `startAfter` and `resumeAfter` are specified. + * + * @param startAfter the startAfter resumeToken + * @return this + * @see [Start After](https://www.mongodb.com/docs/manual/changeStreams/#change-stream-start-after) + */ + public fun startAfter(startAfter: BsonDocument): ChangeStreamIterable = apply { wrapped.startAfter(startAfter) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + */ + public fun comment(comment: String?): ChangeStreamIterable = apply { wrapped.comment(comment) } + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * The comment can be any valid BSON type for server versions 4.4 and above. Server versions between 3.6 and 4.2 + * only support string as comment, and providing a non-string type will result in a server-side error. + * + * @param comment the comment + */ + public fun comment(comment: BsonValue?): ChangeStreamIterable = apply { wrapped.comment(comment) } + + /** + * Sets whether to include expanded change stream events, which are: createIndexes, dropIndexes, modify, create, + * shardCollection, reshardCollection, refineCollectionShardKey. False by default. + * + * @param showExpandedEvents true to include expanded events + * @return this + */ + public fun showExpandedEvents(showExpandedEvents: Boolean): ChangeStreamIterable = apply { + wrapped.showExpandedEvents(showExpandedEvents) + } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt new file mode 100644 index 00000000000..9103689b251 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt @@ -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 com.mongodb.kotlin.client + +import com.mongodb.ClientSessionOptions +import com.mongodb.TransactionOptions +import com.mongodb.client.ClientSession as JClientSession +import java.io.Closeable +import java.util.concurrent.TimeUnit + +/** A client session that supports transactions. */ +public class ClientSession(public val wrapped: JClientSession) : Closeable { + + public override fun close(): Unit = wrapped.close() + + /** The options for this session. */ + public val options: ClientSessionOptions + get() = wrapped.options + + /** Returns true if operations in this session must be causally consistent */ + public val isCausallyConsistent: Boolean + get() = wrapped.isCausallyConsistent + + /** Returns true if there is an active transaction on this session, and false otherwise */ + public val hasActiveTransaction: Boolean + get() = wrapped.hasActiveTransaction() + + /** + * Gets the transaction options. + * + * Only call this method of the session has an active transaction + */ + public val transactionOptions: TransactionOptions + get() = wrapped.transactionOptions + + /** + * Start a transaction in the context of this session with default transaction options. A transaction can not be + * started if there is already an active transaction on this session. + */ + public fun startTransaction(): Unit = wrapped.startTransaction() + + /** + * Start a transaction in the context of this session with the given transaction options. A transaction can not be + * started if there is already an active transaction on this session. + * + * @param transactionOptions the options to apply to the transaction + */ + public fun startTransaction(transactionOptions: TransactionOptions): Unit = + wrapped.startTransaction(transactionOptions) + + /** + * Commit a transaction in the context of this session. A transaction can only be commmited if one has first been + * started. + */ + public fun commitTransaction(): Unit = wrapped.commitTransaction() + + /** + * Abort a transaction in the context of this session. + * + * A transaction can only be aborted if one has first been started. + */ + public fun abortTransaction(): Unit = wrapped.abortTransaction() + + /** + * Execute the given function within a transaction. + * + * @param T the return type of the transaction body + * @param transactionBody the body of the transaction + * @param options the transaction options + * @return the return value of the transaction body + */ + public fun withTransaction( + transactionBody: () -> T, + options: TransactionOptions = TransactionOptions.builder().build() + ): T = wrapped.withTransaction(transactionBody, options) +} + +/** + * maxCommitTime extension function + * + * @param maxCommitTime time in milliseconds + * @return the options + */ +public fun TransactionOptions.Builder.maxCommitTime(maxCommitTime: Long): TransactionOptions.Builder = + this.apply { maxCommitTime(maxCommitTime, TimeUnit.MILLISECONDS) } diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt new file mode 100644 index 00000000000..b630af52517 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt @@ -0,0 +1,85 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.DistinctIterable as JDistinctIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +/** + * Iterable like implementation for distinct operations. + * + * @param T The type of the result. + * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) + */ +public class DistinctIterable(private val wrapped: JDistinctIterable) : MongoIterable(wrapped) { + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): DistinctIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the query filter to apply to the query. + * + * @param filter the filter, which may be null. + * @return this + * @see [Filter results](https://www.mongodb.com/docs/manual/reference/method/db.collection.find/) + */ + public fun filter(filter: Bson?): DistinctIterable = apply { wrapped.filter(filter) } + + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, which defaults to Milliseconds + * @return this + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): DistinctIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * Sets the collation options + * + * A null value represents the server default. + * + * @param collation the collation options to use + * @return this + */ + public fun collation(collation: Collation?): DistinctIterable = apply { wrapped.collation(collation) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): DistinctIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): DistinctIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/FindIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/FindIterable.kt new file mode 100644 index 00000000000..45b51a1e9c9 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/FindIterable.kt @@ -0,0 +1,287 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.CursorType +import com.mongodb.ExplainVerbosity +import com.mongodb.client.FindIterable as JFindIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.Document +import org.bson.conversions.Bson + +/** + * Iterable like implementation for find operations. + * + * @param T The type of the result. + * @see [Collection filter](https://www.mongodb.com/docs/manual/reference/method/db.collection.find/) + */ +public class FindIterable(private val wrapped: JFindIterable) : MongoIterable(wrapped) { + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): FindIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the query filter to apply to the query. + * + * @param filter the filter. + * @return this + * @see [Collection filter](https://www.mongodb.com/docs/manual/reference/method/db.collection.find/) + */ + public fun filter(filter: Bson?): FindIterable = apply { wrapped.filter(filter) } + + /** + * Sets the limit to apply. + * + * @param limit the limit, which may be 0 + * @return this + * @see [Cursor limit](https://www.mongodb.com/docs/manual/reference/method/cursor.limit/#cursor.limit) + */ + public fun limit(limit: Int): FindIterable = apply { wrapped.limit(limit) } + + /** + * Sets the number of documents to skip. + * + * @param skip the number of documents to skip + * @return this + * @see [Cursor skip](https://www.mongodb.com/docs/manual/reference/method/cursor.skip/#cursor.skip) + */ + public fun skip(skip: Int): FindIterable = apply { wrapped.skip(skip) } + + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, which defaults to Milliseconds + * @return this + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): FindIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. This only + * applies to a TAILABLE_AWAIT cursor. When the cursor is not a TAILABLE_AWAIT cursor, this option is ignored. + * + * On servers >= 3.2, this option will be specified on the getMore command as "maxTimeMS". The default is no value: + * no "maxTimeMS" is sent to the server with the getMore command. + * + * On servers < 3.2, this option is ignored, and indicates that the driver should respect the server's default value + * + * A zero value will be ignored. + * + * @param maxAwaitTime the max await time + * @param timeUnit the time unit to return results in, which defaults to Milliseconds + * @return the maximum await execution time in the given time unit + * @see [Max Time](https://www.mongodb.com/docs/manual/reference/method/cursor.maxTimeMS/#cursor.maxTimeMS) + */ + public fun maxAwaitTime(maxAwaitTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): FindIterable = apply { + wrapped.maxAwaitTime(maxAwaitTime, timeUnit) + } + + /** + * Sets a document describing the fields to return for all matching documents. + * + * @param projection the project document. + * @return this + */ + public fun projection(projection: Bson?): FindIterable = apply { wrapped.projection(projection) } + + /** + * Sets the sort criteria to apply to the query. + * + * @param sort the sort criteria. + * @return this + * @see [Cursor sort](https://www.mongodb.com/docs/manual/reference/method/cursor.sort/) + */ + public fun sort(sort: Bson?): FindIterable = apply { wrapped.sort(sort) } + + /** + * The server normally times out idle cursors after an inactivity period (10 minutes) to prevent excess memory use. + * Set this option to prevent that. + * + * @param noCursorTimeout true if cursor timeout is disabled + * @return this + */ + public fun noCursorTimeout(noCursorTimeout: Boolean): FindIterable = apply { + wrapped.noCursorTimeout(noCursorTimeout) + } + + /** + * Users should not set this under normal circumstances. + * + * @param oplogReplay if oplog replay is enabled + * @return this + * @deprecated oplogReplay has been deprecated in MongoDB 4.4. + */ + @Suppress("DEPRECATION") + @Deprecated("oplogReplay has been deprecated in MongoDB 4.4", replaceWith = ReplaceWith("")) + public fun oplogReplay(oplogReplay: Boolean): FindIterable = apply { wrapped.oplogReplay(oplogReplay) } + + /** + * Get partial results from a sharded cluster if one or more shards are unreachable (instead of throwing an error). + * + * @param partial if partial results for sharded clusters is enabled + * @return this + */ + public fun partial(partial: Boolean): FindIterable = apply { wrapped.partial(partial) } + + /** + * Sets the cursor type. + * + * @param cursorType the cursor type + * @return this + */ + public fun cursorType(cursorType: CursorType): FindIterable = apply { wrapped.cursorType(cursorType) } + + /** + * Sets the collation options + * + * A null value represents the server default. + * + * @param collation the collation options to use + * @return this + */ + public fun collation(collation: Collation?): FindIterable = apply { wrapped.collation(collation) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): FindIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * The comment can be any valid BSON type for server versions 4.4 and above. Server versions between 3.6 and 4.2 + * only support string as comment, and providing a non-string type will result in a server-side error. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): FindIterable = apply { wrapped.comment(comment) } + + /** + * Sets the hint for which index to use. A null value means no hint is set. + * + * @param hint the hint + * @return this + */ + public fun hint(hint: Bson?): FindIterable = apply { wrapped.hint(hint) } + + /** + * Sets the hint to apply. + * + * Note: If [FindIterable.hint] is set that will be used instead of any hint string. + * + * @param hint the name of the index which should be used for the operation + * @return this + */ + public fun hintString(hint: String?): FindIterable = apply { wrapped.hintString(hint) } + + /** + * Add top-level variables to the operation. A null value means no variables are set. + * + * Allows for improved command readability by separating the variables from the query text. + * + * @param variables for find operation + * @return this + */ + public fun let(variables: Bson?): FindIterable = apply { wrapped.let(variables) } + + /** + * Sets the exclusive upper bound for a specific index. A null value means no max is set. + * + * @param max the max + * @return this + */ + public fun max(max: Bson?): FindIterable = apply { wrapped.max(max) } + + /** + * Sets the minimum inclusive lower bound for a specific index. A null value means no max is set. + * + * @param min the min + * @return this + */ + public fun min(min: Bson?): FindIterable = apply { wrapped.min(min) } + + /** + * Sets the returnKey. If true the find operation will return only the index keys in the resulting documents. + * + * @param returnKey the returnKey + * @return this + */ + public fun returnKey(returnKey: Boolean): FindIterable = apply { wrapped.returnKey(returnKey) } + + /** + * Sets the showRecordId. Set to true to add a field `$recordId` to the returned documents. + * + * @param showRecordId the showRecordId + * @return this + */ + public fun showRecordId(showRecordId: Boolean): FindIterable = apply { wrapped.showRecordId(showRecordId) } + + /** + * Enables writing to temporary files on the server. When set to true, the server can write temporary data to disk + * while executing the find operation. + * + * This option is sent only if the caller explicitly sets it to true. + * + * @param allowDiskUse the allowDiskUse + * @return this + */ + public fun allowDiskUse(allowDiskUse: Boolean?): FindIterable = apply { wrapped.allowDiskUse(allowDiskUse) } + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public fun explain(verbosity: ExplainVerbosity? = null): Document = explain(verbosity) + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param R the type of the document class + * @param resultClass the result document type. + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public fun explain(resultClass: Class, verbosity: ExplainVerbosity? = null): R = + if (verbosity == null) wrapped.explain(resultClass) else wrapped.explain(resultClass, verbosity) + + /** + * Explain the execution plan for this operation with the given verbosity level + * + * @param R the type of the document class + * @param verbosity the verbosity of the explanation + * @return the execution plan + * @see [Explain command](https://www.mongodb.com/docs/manual/reference/command/explain/) + */ + public inline fun explain(verbosity: ExplainVerbosity? = null): R = + explain(R::class.java, verbosity) +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListCollectionsIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListCollectionsIterable.kt new file mode 100644 index 00000000000..6ff8bc9c3fa --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListCollectionsIterable.kt @@ -0,0 +1,75 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ListCollectionsIterable as JListCollectionsIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +/** + * Iterable like implementation for list collection operations. + * + * @param T The type of the result. + * @see [List collections](https://www.mongodb.com/docs/manual/reference/command/listCollections/) + */ +public class ListCollectionsIterable(private val wrapped: JListCollectionsIterable) : + MongoIterable(wrapped) { + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, defaults to Milliseconds + * @return this + * @see [Max Time](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): ListCollectionsIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): ListCollectionsIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the query filter to apply to the returned database names. + * + * @param filter the filter, which may be null. + * @return this + */ + public fun filter(filter: Bson?): ListCollectionsIterable = apply { wrapped.filter(filter) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): ListCollectionsIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): ListCollectionsIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListDatabasesIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListDatabasesIterable.kt new file mode 100644 index 00000000000..560920b5e0d --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListDatabasesIterable.kt @@ -0,0 +1,94 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ListDatabasesIterable as JListDatabasesIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue +import org.bson.conversions.Bson + +/** + * Iterable like implementation for list database operations. + * + * @param T The type of the result. + * @see [List databases](https://www.mongodb.com/docs/manual/reference/command/listDatabases/) + */ +public class ListDatabasesIterable(private val wrapped: JListDatabasesIterable) : + MongoIterable(wrapped) { + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, defaults to Milliseconds + * @return this + * @see [Max Time](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): ListDatabasesIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): ListDatabasesIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the query filter to apply to the returned database names. + * + * @param filter the filter, which may be null. + * @return this + */ + public fun filter(filter: Bson?): ListDatabasesIterable = apply { wrapped.filter(filter) } + /** + * Sets the nameOnly flag that indicates whether the command should return just the database names or return the + * database names and size information. + * + * @param nameOnly the nameOnly flag, which may be null + * @return this + */ + public fun nameOnly(nameOnly: Boolean?): ListDatabasesIterable = apply { wrapped.nameOnly(nameOnly) } + + /** + * Sets the authorizedDatabasesOnly flag that indicates whether the command should return just the databases which + * the user is authorized to see. + * + * @param authorizedDatabasesOnly the authorizedDatabasesOnly flag, which may be null + * @return this + */ + public fun authorizedDatabasesOnly(authorizedDatabasesOnly: Boolean?): ListDatabasesIterable = apply { + wrapped.authorizedDatabasesOnly(authorizedDatabasesOnly) + } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): ListDatabasesIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): ListDatabasesIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListIndexesIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListIndexesIterable.kt new file mode 100644 index 00000000000..36847cb49d8 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ListIndexesIterable.kt @@ -0,0 +1,65 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ListIndexesIterable as JListIndexesIterable +import java.util.concurrent.TimeUnit +import org.bson.BsonValue + +/** + * Iterable like implementation for list index operations. + * + * @param T The type of the result. + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ +public class ListIndexesIterable(private val wrapped: JListIndexesIterable) : MongoIterable(wrapped) { + /** + * Sets the maximum execution time on the server for this operation. + * + * @param maxTime the max time + * @param timeUnit the time unit, defaults to Milliseconds + * @return this + * @see [Max Time](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) + */ + public fun maxTime(maxTime: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): ListIndexesIterable = apply { + wrapped.maxTime(maxTime, timeUnit) + } + + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public override fun batchSize(batchSize: Int): ListIndexesIterable = apply { wrapped.batchSize(batchSize) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: String?): ListIndexesIterable = apply { wrapped.comment(comment) } + + /** + * Sets the comment for this operation. A null value means no comment is set. + * + * @param comment the comment + * @return this + */ + public fun comment(comment: BsonValue?): ListIndexesIterable = apply { wrapped.comment(comment) } +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt new file mode 100644 index 00000000000..4cae28c973f --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoClient.kt @@ -0,0 +1,282 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ClientSessionOptions +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.MongoDriverInformation +import com.mongodb.client.MongoClient as JMongoClient +import com.mongodb.client.MongoClients as JMongoClients +import com.mongodb.connection.ClusterDescription +import java.io.Closeable +import org.bson.Document +import org.bson.conversions.Bson + +/** + * A client-side representation of a MongoDB cluster. + * + * Instances can represent either a standalone MongoDB instance, a replica set, or a sharded cluster. Instance of this + * class are responsible for maintaining an up-to-date state of the cluster, and possibly cache resources related to + * this, including background threads for monitoring, and connection pools. + * + * Instances of this class serve as factories for [MongoDatabase] instances. Instances of this class can be created via + * the [MongoClient.create] helpers + * + * @see MongoClient.create + */ +public class MongoClient(private val wrapped: JMongoClient) : Closeable { + + /** + * A factory for [MongoClient] instances. + * + * @see MongoClient + */ + public companion object Factory { + /** + * Create a new client with the given connection string as if by a call to [create]. + * + * @param connectionString the connection + * @return the client + */ + public fun create(connectionString: String): MongoClient = create(ConnectionString(connectionString)) + + /** + * Create a new client with the given connection string. + * + * @param connectionString the connection string, defaults to `mongodb://localhost`. + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @return the client + */ + public fun create( + connectionString: ConnectionString = ConnectionString("mongodb://localhost"), + mongoDriverInformation: MongoDriverInformation? = null + ): MongoClient { + return create( + MongoClientSettings.builder().applyConnectionString(connectionString).build(), mongoDriverInformation) + } + + /** + * Create a new client with the given connection string. + * + * For each of the settings classed configurable via [MongoClientSettings], the connection string is applied by + * calling the `applyConnectionString` method on an instance of setting's builder class, building the setting, + * and adding it to an instance of [com.mongodb.MongoClientSettings.Builder]. + * + * @param settings the client settings + * @param mongoDriverInformation any driver information to associate with the MongoClient + * @return + */ + public fun create( + settings: MongoClientSettings, + mongoDriverInformation: MongoDriverInformation? = null + ): MongoClient { + val builder = + if (mongoDriverInformation == null) MongoDriverInformation.builder() + else MongoDriverInformation.builder(mongoDriverInformation) + return MongoClient(JMongoClients.create(settings, builder.driverName("kotlin").build())) + } + } + + public override fun close(): Unit = wrapped.close() + + /** + * Gets the current cluster description. + * + * This method will not block, meaning that it may return a [ClusterDescription] whose `clusterType` is unknown and + * whose [com.mongodb.connection.ServerDescription]s are all in the connecting state. If the application requires + * notifications after the driver has connected to a member of the cluster, it should register a + * [com.mongodb.event.ClusterListener] via the [com.mongodb.connection.ClusterSettings] in + * [com.mongodb.MongoClientSettings]. + * + * @return the current cluster description + * @see com.mongodb.connection.ClusterSettings.Builder.addClusterListener + * @see com.mongodb.MongoClientSettings.Builder.applyToClusterSettings + */ + public val clusterDescription: ClusterDescription + get() = wrapped.clusterDescription + + /** + * Gets a [MongoDatabase] instance for the given database name. + * + * @param databaseName the name of the database to retrieve + * @return a `MongoDatabase` representing the specified database + * @throws IllegalArgumentException if databaseName is invalid + * @see com.mongodb.MongoNamespace.checkDatabaseNameValidity + */ + public fun getDatabase(databaseName: String): MongoDatabase = MongoDatabase(wrapped.getDatabase(databaseName)) + + /** + * Creates a client session. + * + * Note: A ClientSession instance can not be used concurrently in multiple operations. + * + * @param options the options for the client session + * @return the client session + */ + public fun startSession(options: ClientSessionOptions = ClientSessionOptions.builder().build()): ClientSession = + ClientSession(wrapped.startSession(options)) + + /** + * Get a list of the database names + * + * @return an iterable containing all the names of all the databases + * @see [List Databases](https://www.mongodb.com/docs/manual/reference/command/listDatabases) + */ + public fun listDatabaseNames(): MongoIterable = MongoIterable(wrapped.listDatabaseNames()) + + /** + * Gets the list of databases + * + * @param clientSession the client session with which to associate this operation + * @return the list databases iterable interface + * @see [List Databases](https://www.mongodb.com/docs/manual/reference/command/listDatabases) + */ + public fun listDatabaseNames(clientSession: ClientSession): MongoIterable = + MongoIterable(wrapped.listDatabaseNames(clientSession.wrapped)) + + /** + * Gets the list of databases + * + * @return the list databases iterable interface + */ + @JvmName("listDatabasesAsDocument") + public fun listDatabases(): ListDatabasesIterable = listDatabases() + + /** + * Gets the list of databases + * + * @param clientSession the client session with which to associate this operation + * @return the list databases iterable interface + */ + @JvmName("listDatabasesAsDocumentWithSession") + public fun listDatabases(clientSession: ClientSession): ListDatabasesIterable = + listDatabases(clientSession) + + /** + * Gets the list of databases + * + * @param T the type of the class to use + * @param resultClass the target document type of the iterable. + * @return the list databases iterable interface + */ + public fun listDatabases(resultClass: Class): ListDatabasesIterable = + ListDatabasesIterable(wrapped.listDatabases(resultClass)) + + /** + * Gets the list of databases + * + * @param T the type of the class to use + * @param clientSession the client session with which to associate this operation + * @param resultClass the target document type of the iterable. + * @return the list databases iterable interface + */ + public fun listDatabases(clientSession: ClientSession, resultClass: Class): ListDatabasesIterable = + ListDatabasesIterable(wrapped.listDatabases(clientSession.wrapped, resultClass)) + + /** + * Gets the list of databases + * + * @param T the type of the class to use + * @return the list databases iterable interface + */ + public inline fun listDatabases(): ListDatabasesIterable = listDatabases(T::class.java) + + /** + * Gets the list of databases + * + * @param clientSession the client session with which to associate this operation + * @param T the type of the class to use + * @return the list databases iterable interface + */ + public inline fun listDatabases(clientSession: ClientSession): ListDatabasesIterable = + listDatabases(clientSession, T::class.java) + + /** + * Creates a change stream for this client. + * + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocument") + public fun watch(pipeline: List = emptyList()): ChangeStreamIterable = watch(pipeline) + + /** + * Creates a change stream for this client. + * + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocumentWithSession") + public fun watch(clientSession: ClientSession, pipeline: List = emptyList()): ChangeStreamIterable = + watch(clientSession, pipeline) + + /** + * Creates a change stream for this client. + * + * @param T the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch(pipeline: List = emptyList(), resultClass: Class): ChangeStreamIterable = + ChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + /** + * Creates a change stream for this client. + * + * @param T the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch( + clientSession: ClientSession, + pipeline: List = emptyList(), + resultClass: Class + ): ChangeStreamIterable = ChangeStreamIterable(wrapped.watch(clientSession.wrapped, pipeline, resultClass)) + + /** + * Creates a change stream for this client. + * + * @param T the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch(pipeline: List = emptyList()): ChangeStreamIterable = + watch(pipeline, T::class.java) + + /** + * Creates a change stream for this client. + * + * @param T the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch( + clientSession: ClientSession, + pipeline: List = emptyList() + ): ChangeStreamIterable = watch(clientSession, pipeline, T::class.java) +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt new file mode 100644 index 00000000000..c2cf060b754 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt @@ -0,0 +1,1381 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.MongoNamespace +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.bulk.BulkWriteResult +import com.mongodb.client.MongoCollection as JMongoCollection +import com.mongodb.client.model.BulkWriteOptions +import com.mongodb.client.model.CountOptions +import com.mongodb.client.model.CreateIndexOptions +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 +import com.mongodb.client.model.FindOneAndReplaceOptions +import com.mongodb.client.model.FindOneAndUpdateOptions +import com.mongodb.client.model.IndexModel +import com.mongodb.client.model.IndexOptions +import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.InsertOneOptions +import com.mongodb.client.model.RenameCollectionOptions +import com.mongodb.client.model.ReplaceOptions +import com.mongodb.client.model.UpdateOptions +import com.mongodb.client.model.WriteModel +import com.mongodb.client.result.DeleteResult +import com.mongodb.client.result.InsertManyResult +import com.mongodb.client.result.InsertOneResult +import com.mongodb.client.result.UpdateResult +import java.util.concurrent.TimeUnit +import org.bson.BsonDocument +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.bson.conversions.Bson + +/** + * The MongoCollection representation. + * + * Note: Additions to this interface will not be considered to break binary compatibility. + * + * @param T The type of documents the collection will encode documents from and decode documents to. + * @property wrapped the underlying sync MongoCollection + */ +public class MongoCollection(private val wrapped: JMongoCollection) { + + /** The class of documents stored in this collection. */ + public val documentClass: Class + get() = wrapped.documentClass + + /** The namespace of this collection. */ + public val namespace: MongoNamespace + get() = wrapped.namespace + + /** The codec registry for the collection. */ + public val codecRegistry: CodecRegistry + get() = wrapped.codecRegistry + + /** the read preference for the collection. */ + public val readPreference: ReadPreference + get() = wrapped.readPreference + + /** The read concern for the collection. */ + public val readConcern: ReadConcern + get() = wrapped.readConcern + + /** The write concern for the collection. */ + public val writeConcern: WriteConcern + get() = wrapped.writeConcern + + /** + * Create a new collection instance with a different default class to cast any documents returned from the database + * into. + * + * @param R the default class to cast any documents returned from the database into. + * @param resultClass the target document type for the collection. + * @return a new MongoCollection instance with the different default class + */ + public fun withDocumentClass(resultClass: Class): MongoCollection = + MongoCollection(wrapped.withDocumentClass(resultClass)) + + /** + * Create a new collection instance with a different default class to cast any documents returned from the database + * into. + * + * @param R the default class to cast any documents returned from the database into. + * @return a new MongoCollection instance with the different default class + */ + public inline fun withDocumentClass(): MongoCollection = withDocumentClass(R::class.java) + + /** + * Create a new collection instance with a different codec registry. + * + * The [CodecRegistry] configured by this method is effectively treated by the driver as an instance of + * [org.bson.codecs.configuration.CodecProvider], which [CodecRegistry] extends. So there is no benefit to defining + * a class that implements [CodecRegistry]. Rather, an application should always create [CodecRegistry] instances + * using the factory methods in [org.bson.codecs.configuration.CodecRegistries]. + * + * @param newCodecRegistry the new [org.bson.codecs.configuration.CodecRegistry] for the collection + * @return a new MongoCollection instance with the different codec registry + * @see org.bson.codecs.configuration.CodecRegistries + */ + public fun withCodecRegistry(newCodecRegistry: CodecRegistry): MongoCollection = + MongoCollection(wrapped.withCodecRegistry(newCodecRegistry)) + + /** + * Create a new collection instance with a different read preference. + * + * @param newReadPreference the new [com.mongodb.ReadPreference] for the collection + * @return a new MongoCollection instance with the different readPreference + */ + public fun withReadPreference(newReadPreference: ReadPreference): MongoCollection = + MongoCollection(wrapped.withReadPreference(newReadPreference)) + + /** + * Create a new collection instance with a different read concern. + * + * @param newReadConcern the new [ReadConcern] for the collection + * @return a new MongoCollection instance with the different ReadConcern + * @see [Read Concern](https://www.mongodb.com/docs/manual/reference/readConcern/) + */ + public fun withReadConcern(newReadConcern: ReadConcern): MongoCollection = + MongoCollection(wrapped.withReadConcern(newReadConcern)) + + /** + * Create a new collection instance with a different write concern. + * + * @param newWriteConcern the new [com.mongodb.WriteConcern] for the collection + * @return a new MongoCollection instance with the different writeConcern + */ + public fun withWriteConcern(newWriteConcern: WriteConcern): MongoCollection = + MongoCollection(wrapped.withWriteConcern(newWriteConcern)) + + /** + * Counts the number of documents in the collection. + * + * Note: For a fast count of the total documents in a collection see [estimatedDocumentCount]. When migrating from + * `count()` to `countDocuments()` the following query operators must be replaced: + * ``` + * +-------------+--------------------------------+ + * | Operator | Replacement | + * +=============+================================+ + * | $where | $expr | + * +-------------+--------------------------------+ + * | $near | $geoWithin with $center | + * +-------------+--------------------------------+ + * | $nearSphere | $geoWithin with $centerSphere | + * +-------------+--------------------------------+ + * ``` + * + * @return the number of documents in the collection + */ + public fun countDocuments(filter: Bson = BsonDocument(), options: CountOptions = CountOptions()): Long = + wrapped.countDocuments(filter, options) + + /** + * Counts the number of documents in the collection according to the given options. + * + * Note: For a fast count of the total documents in a collection see [estimatedDocumentCount]. When migrating from + * `count()` to `countDocuments()` the following query operators must be replaced: + * ``` + * +-------------+--------------------------------+ + * | Operator | Replacement | + * +=============+================================+ + * | $where | $expr | + * +-------------+--------------------------------+ + * | $near | $geoWithin with $center | + * +-------------+--------------------------------+ + * | $nearSphere | $geoWithin with $centerSphere | + * +-------------+--------------------------------+ + * ``` + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter + * @param options the options describing the count + * @return the number of documents in the collection + */ + public fun countDocuments( + clientSession: ClientSession, + filter: Bson = BsonDocument(), + options: CountOptions = CountOptions() + ): Long = wrapped.countDocuments(clientSession.wrapped, filter, options) + + /** + * Gets an estimate of the count of documents in a collection using collection metadata. + * + * Implementation note: this method is implemented using the MongoDB server's count command + * + * @param options the options describing the count + * @return the number of documents in the collection + * @see [Count behaviour](https://www.mongodb.com/docs/manual/reference/command/count/#behavior) + */ + public fun estimatedDocumentCount(options: EstimatedDocumentCountOptions = EstimatedDocumentCountOptions()): Long = + wrapped.estimatedDocumentCount(options) + + /** + * Gets the distinct values of the specified field name. + * + * @param R the target type of the iterable. + * @param fieldName the field name + * @param filter the query filter + * @param resultClass the target document type of the iterable. + * @return an iterable of distinct values + * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) + */ + public fun distinct( + fieldName: String, + filter: Bson = BsonDocument(), + resultClass: Class + ): DistinctIterable = DistinctIterable(wrapped.distinct(fieldName, filter, resultClass)) + + /** + * Gets the distinct values of the specified field name. + * + * @param R the target type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param fieldName the field name + * @param filter the query filter + * @param resultClass the target document type of the iterable. + * @return an iterable of distinct values + * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) + */ + public fun distinct( + clientSession: ClientSession, + fieldName: String, + filter: Bson = BsonDocument(), + resultClass: Class + ): DistinctIterable = DistinctIterable(wrapped.distinct(clientSession.wrapped, fieldName, filter, resultClass)) + + /** + * Gets the distinct values of the specified field name. + * + * @param R the target type of the iterable. + * @param fieldName the field name + * @param filter the query filter + * @return an iterable of distinct values + * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) + */ + public inline fun distinct( + fieldName: String, + filter: Bson = BsonDocument() + ): DistinctIterable = distinct(fieldName, filter, R::class.java) + + /** + * Gets the distinct values of the specified field name. + * + * @param R the target type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param fieldName the field name + * @param filter the query filter + * @return an iterable of distinct values + * @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/) + */ + public inline fun distinct( + clientSession: ClientSession, + fieldName: String, + filter: Bson = BsonDocument() + ): DistinctIterable = distinct(clientSession, fieldName, filter, R::class.java) + + /** + * Finds all documents in the collection. + * + * @param filter the query filter + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + @JvmName("findAsT") public fun find(filter: Bson = BsonDocument()): FindIterable = find(filter, documentClass) + + /** + * Finds all documents in the collection. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + @JvmName("findAsTWithSession") + public fun find(clientSession: ClientSession, filter: Bson = BsonDocument()): FindIterable = + find(clientSession, filter, documentClass) + + /** + * Finds all documents in the collection. + * + * @param R the class to decode each document into + * @param filter the query filter + * @param resultClass the target document type of the iterable. + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + public fun find(filter: Bson = BsonDocument(), resultClass: Class): FindIterable = + FindIterable(wrapped.find(filter, resultClass)) + + /** + * Finds all documents in the collection. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param filter the query filter + * @param resultClass the target document type of the iterable. + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + public fun find( + clientSession: ClientSession, + filter: Bson = BsonDocument(), + resultClass: Class + ): FindIterable = FindIterable(wrapped.find(clientSession.wrapped, filter, resultClass)) + + /** + * Finds all documents in the collection. + * + * @param R the class to decode each document into + * @param filter the query filter + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + public inline fun find(filter: Bson = BsonDocument()): FindIterable = + find(filter, R::class.java) + + /** + * Finds all documents in the collection. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param filter the query filter + * @return the find iterable interface + * @see [Query Documents](https://www.mongodb.com/docs/manual/tutorial/query-documents/) + */ + public inline fun find( + clientSession: ClientSession, + filter: Bson = BsonDocument() + ): FindIterable = find(clientSession, filter, R::class.java) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + @JvmName("aggregateAsT") + public fun aggregate(pipeline: List): AggregateIterable = + AggregateIterable(wrapped.aggregate(pipeline, documentClass)) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + @JvmName("aggregateAsTWithSession") + public fun aggregate(clientSession: ClientSession, pipeline: List): AggregateIterable = + AggregateIterable(wrapped.aggregate(clientSession.wrapped, pipeline, documentClass)) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param R the class to decode each document into + * @param pipeline the aggregation pipeline + * @param resultClass the target document type of the iterable. + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + public fun aggregate(pipeline: List, resultClass: Class): AggregateIterable = + AggregateIterable(wrapped.aggregate(pipeline, resultClass)) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @param resultClass the target document type of the iterable. + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + public fun aggregate( + clientSession: ClientSession, + pipeline: List, + resultClass: Class + ): AggregateIterable = AggregateIterable(wrapped.aggregate(clientSession.wrapped, pipeline, resultClass)) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param R the class to decode each document into + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + public inline fun aggregate(pipeline: List): AggregateIterable = + aggregate(pipeline, R::class.java) + + /** + * Aggregates documents according to the specified aggregation pipeline. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate/) + */ + public inline fun aggregate( + clientSession: ClientSession, + pipeline: List + ): AggregateIterable = aggregate(clientSession, pipeline, R::class.java) + + /** + * Creates a change stream for this collection. + * + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocument") + public fun watch(pipeline: List = emptyList()): ChangeStreamIterable = watch(pipeline, documentClass) + + /** + * Creates a change stream for this collection. + * + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocumentWithSession") + public fun watch(clientSession: ClientSession, pipeline: List = emptyList()): ChangeStreamIterable = + watch(clientSession, pipeline, documentClass) + + /** + * Creates a change stream for this collection. + * + * @param R the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch(pipeline: List = emptyList(), resultClass: Class): ChangeStreamIterable = + ChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + /** + * Creates a change stream for this collection. + * + * @param R the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch( + clientSession: ClientSession, + pipeline: List = emptyList(), + resultClass: Class + ): ChangeStreamIterable = ChangeStreamIterable(wrapped.watch(clientSession.wrapped, pipeline, resultClass)) + + /** + * Creates a change stream for this collection. + * + * @param R the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch(pipeline: List = emptyList()): ChangeStreamIterable = + watch(pipeline, R::class.java) + + /** + * Creates a change stream for this collection. + * + * @param R the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch( + clientSession: ClientSession, + pipeline: List = emptyList() + ): ChangeStreamIterable = watch(clientSession, pipeline, R::class.java) + + /** + * Inserts the provided document. If the document is missing an identifier, the driver should generate one. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param document the document to insert + * @param options the options to apply to the operation + * @return the insert one result + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun insertOne(document: T, options: InsertOneOptions = InsertOneOptions()): InsertOneResult = + wrapped.insertOne(document, options) + + /** + * Inserts the provided document. If the document is missing an identifier, the driver should generate one. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param document the document to insert + * @param options the options to apply to the operation + * @return the insert one result + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun insertOne( + clientSession: ClientSession, + document: T, + options: InsertOneOptions = InsertOneOptions() + ): InsertOneResult = wrapped.insertOne(clientSession.wrapped, document, options) + + /** + * Inserts one or more documents. A call to this method is equivalent to a call to the `bulkWrite` method + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param documents the documents to insert + * @param options the options to apply to the operation + * @return the insert many result + * @throws com.mongodb.MongoBulkWriteException if there's an exception in the bulk write operation + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @throws IllegalArgumentException if the documents list is null or empty, or any of the documents in the list are + * null + */ + public fun insertMany(documents: List, options: InsertManyOptions = InsertManyOptions()): InsertManyResult = + wrapped.insertMany(documents, options) + + /** + * Inserts one or more documents. A call to this method is equivalent to a call to the `bulkWrite` method + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param documents the documents to insert + * @param options the options to apply to the operation + * @return the insert many result + * @throws com.mongodb.MongoBulkWriteException if there's an exception in the bulk write operation + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @throws IllegalArgumentException if the documents list is null or empty, or any of the documents in the list are + * null + */ + public fun insertMany( + clientSession: ClientSession, + documents: List, + options: InsertManyOptions = InsertManyOptions() + ): InsertManyResult = wrapped.insertMany(clientSession.wrapped, documents, options) + + /** + * Update a single document in the collection according to the specified arguments. + * + * Use this method to only update the corresponding fields in the document according to the update operators used in + * the update document. To replace the entire document with a new document, use the corresponding [replaceOne] + * method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include at least + * one update operator. + * @param options the options to apply to the update operation + * @return the result of the update one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + * @see [replaceOne] + */ + public fun updateOne(filter: Bson, update: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult = + wrapped.updateOne(filter, update, options) + + /** + * Update a single document in the collection according to the specified arguments. + * + * Use this method to only update the corresponding fields in the document according to the update operators used in + * the update document. To replace the entire document with a new document, use the corresponding [replaceOne] + * method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include at least + * one update operator. + * @param options the options to apply to the update operation + * @return the result of the update one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + * @see [Update Command](https://www.mongodb.com/docs/manual/reference/command/update/) + * @see com.mongodb.client.MongoCollection.replaceOne + */ + public fun updateOne( + clientSession: ClientSession, + filter: Bson, + update: Bson, + options: UpdateOptions = UpdateOptions() + ): UpdateResult = wrapped.updateOne(clientSession.wrapped, filter, update, options) + + /** + * Update a single document in the collection according to the specified arguments. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the update operation + * @return the result of the update one operation + * @throws com.mongodb.MongoWriteException if the write failed due some other failure specific to the update command + * @throws com.mongodb.MongoWriteConcernException if the write failed due being unable to fulfil the write concern + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateOne(filter: Bson, update: List, options: UpdateOptions = UpdateOptions()): UpdateResult = + wrapped.updateOne(filter, update, options) + + /** + * Update a single document in the collection according to the specified arguments. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the update operation + * @return the result of the update one operation + * @throws com.mongodb.MongoWriteException if the write failed due some other failure specific to the update command + * @throws com.mongodb.MongoWriteConcernException if the write failed due being unable to fulfil the write concern + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateOne( + clientSession: ClientSession, + filter: Bson, + update: List, + options: UpdateOptions = UpdateOptions() + ): UpdateResult = wrapped.updateOne(clientSession.wrapped, filter, update, options) + + /** + * Update all documents in the collection according to the specified arguments. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include only + * update operators. + * @param options the options to apply to the update operation + * @return the result of the update many operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateMany(filter: Bson, update: Bson, options: UpdateOptions = UpdateOptions()): UpdateResult = + wrapped.updateMany(filter, update, options) + + /** + * Update all documents in the collection according to the specified arguments. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include only + * update operators. + * @param options the options to apply to the update operation + * @return the result of the update many operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateMany( + clientSession: ClientSession, + filter: Bson, + update: Bson, + options: UpdateOptions = UpdateOptions() + ): UpdateResult = wrapped.updateMany(clientSession.wrapped, filter, update, options) + + /** + * Update all documents in the collection according to the specified arguments. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the update operation + * @return the result of the update many operation + * @throws com.mongodb.MongoWriteException if the write failed due some other failure specific to the update command + * @throws com.mongodb.MongoWriteConcernException if the write failed due being unable to fulfil the write concern + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateMany(filter: Bson, update: List, options: UpdateOptions = UpdateOptions()): UpdateResult = + wrapped.updateMany(filter, update, options) + + /** + * Update all documents in the collection according to the specified arguments. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the update operation + * @return the result of the update many operation + * @throws com.mongodb.MongoWriteException if the write failed due some other failure specific to the update command + * @throws com.mongodb.MongoWriteConcernException if the write failed due being unable to fulfil the write concern + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/) + * @see [Update Operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + */ + public fun updateMany( + clientSession: ClientSession, + filter: Bson, + update: List, + options: UpdateOptions = UpdateOptions() + ): UpdateResult = wrapped.updateMany(clientSession.wrapped, filter, update, options) + + /** + * Replace a document in the collection according to the specified arguments. + * + * Use this method to replace a document using the specified replacement argument. To update the document with + * update operators, use the corresponding [updateOne] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter the query filter to apply the replace operation + * @param replacement the replacement document + * @param options the options to apply to the replace operation + * @return the result of the replace one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/#replace-the-document/) + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + */ + public fun replaceOne(filter: Bson, replacement: T, options: ReplaceOptions = ReplaceOptions()): UpdateResult = + wrapped.replaceOne(filter, replacement, options) + + /** + * Replace a document in the collection according to the specified arguments. + * + * Use this method to replace a document using the specified replacement argument. To update the document with + * update operators, use the corresponding [updateOne] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter to apply the replace operation + * @param replacement the replacement document + * @param options the options to apply to the replace operation + * @return the result of the replace one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + * @see [Modify Documents](https://www.mongodb.com/docs/manual/tutorial/modify-documents/#replace-the-document/) + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + */ + public fun replaceOne( + clientSession: ClientSession, + filter: Bson, + replacement: T, + options: ReplaceOptions = ReplaceOptions() + ): UpdateResult = wrapped.replaceOne(clientSession.wrapped, filter, replacement, options) + + /** + * Removes at most one document from the collection that matches the given filter. + * + * If no documents match, the collection is not modified. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter the query filter to apply the delete operation + * @param options the options to apply to the delete operation + * @return the result of the remove one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun deleteOne(filter: Bson, options: DeleteOptions = DeleteOptions()): DeleteResult = + wrapped.deleteOne(filter, options) + + /** + * Removes at most one document from the collection that matches the given filter. + * + * If no documents match, the collection is not modified. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter to apply the delete operation + * @param options the options to apply to the delete operation + * @return the result of the remove one operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun deleteOne( + clientSession: ClientSession, + filter: Bson, + options: DeleteOptions = DeleteOptions() + ): DeleteResult = wrapped.deleteOne(clientSession.wrapped, filter, options) + + /** + * Removes all documents from the collection that match the given query filter. + * + * If no documents match, the collection is not modified. + * + * @param filter the query filter to apply the delete operation + * @param options the options to apply to the delete operation + * @return the result of the remove many operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun deleteMany(filter: Bson, options: DeleteOptions = DeleteOptions()): DeleteResult = + wrapped.deleteMany(filter, options) + + /** + * Removes all documents from the collection that match the given query filter. + * + * If no documents match, the collection is not modified. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter to apply the delete operation + * @param options the options to apply to the delete operation + * @return the result of the remove many operation + * @throws com.mongodb.MongoWriteException if the write failed due to some specific write exception + * @throws com.mongodb.MongoWriteConcernException if the write failed due to being unable to fulfil the write + * concern + * @throws com.mongodb.MongoCommandException if the write failed due to a specific command exception + * @throws com.mongodb.MongoException if the write failed due some other failure + */ + public fun deleteMany( + clientSession: ClientSession, + filter: Bson, + options: DeleteOptions = DeleteOptions() + ): DeleteResult = wrapped.deleteMany(clientSession.wrapped, filter, options) + + /** + * Executes a mix of inserts, updates, replaces, and deletes. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * The eligibility for retryable write support for bulk operations is determined on the whole bulk write. If the + * `requests` contain any `UpdateManyModels` or `DeleteManyModels` then the bulk operation will not support + * retryable writes. + * + * @param requests the writes to execute + * @param options the options to apply to the bulk write operation + * @return the result of the bulk write + * @throws com.mongodb.MongoBulkWriteException if there's an exception in the bulk write operation + * @throws com.mongodb.MongoException if there's an exception running the operation + */ + public fun bulkWrite( + requests: List>, + options: BulkWriteOptions = BulkWriteOptions() + ): BulkWriteResult = wrapped.bulkWrite(requests, options) + + /** + * Executes a mix of inserts, updates, replaces, and deletes. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * The eligibility for retryable write support for bulk operations is determined on the whole bulk write. If the + * `requests` contain any `UpdateManyModels` or `DeleteManyModels` then the bulk operation will not support + * retryable writes. + * + * @param clientSession the client session with which to associate this operation + * @param requests the writes to execute + * @param options the options to apply to the bulk write operation + * @return the result of the bulk write + * @throws com.mongodb.MongoBulkWriteException if there's an exception in the bulk write operation + * @throws com.mongodb.MongoException if there's an exception running the operation + */ + public fun bulkWrite( + clientSession: ClientSession, + requests: List>, + options: BulkWriteOptions = BulkWriteOptions() + ): BulkWriteResult = wrapped.bulkWrite(clientSession.wrapped, requests, options) + + /** + * Atomically find a document and remove it. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter the query filter to find the document with + * @param options the options to apply to the operation + * @return the document that was removed. If no documents matched the query filter, then null will be returned + */ + public fun findOneAndDelete(filter: Bson, options: FindOneAndDeleteOptions = FindOneAndDeleteOptions()): T? = + wrapped.findOneAndDelete(filter, options) + + /** + * Atomically find a document and remove it. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter to find the document with + * @param options the options to apply to the operation + * @return the document that was removed. If no documents matched the query filter, then null will be returned + */ + public fun findOneAndDelete( + clientSession: ClientSession, + filter: Bson, + options: FindOneAndDeleteOptions = FindOneAndDeleteOptions() + ): T? = wrapped.findOneAndDelete(clientSession.wrapped, filter, options) + + /** + * Atomically find a document and update it. + * + * Use this method to only update the corresponding fields in the document according to the update operators used in + * the update document. To replace the entire document with a new document, use the corresponding + * [findOneAndReplace] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include at least + * one update operator. + * @param options the options to apply to the operation + * @return the document that was updated. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + * @see com.mongodb.client.MongoCollection.findOneAndReplace + */ + public fun findOneAndUpdate( + filter: Bson, + update: Bson, + options: FindOneAndUpdateOptions = FindOneAndUpdateOptions() + ): T? = wrapped.findOneAndUpdate(filter, update, options) + + /** + * Atomically find a document and update it. + * + * Use this method to only update the corresponding fields in the document according to the update operators used in + * the update document. To replace the entire document with a new document, use the corresponding + * [findOneAndReplace] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a document describing the update, which may not be null. The update to apply must include at least + * one update operator. + * @param options the options to apply to the operation + * @return the document that was updated. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + * @see com.mongodb.client.MongoCollection.findOneAndReplace + */ + public fun findOneAndUpdate( + clientSession: ClientSession, + filter: Bson, + update: Bson, + options: FindOneAndUpdateOptions = FindOneAndUpdateOptions() + ): T? = wrapped.findOneAndUpdate(clientSession.wrapped, filter, update, options) + + /** + * Atomically find a document and update it. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the operation + * @return the document that was updated. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + */ + public fun findOneAndUpdate( + filter: Bson, + update: List, + options: FindOneAndUpdateOptions = FindOneAndUpdateOptions() + ): T? = wrapped.findOneAndUpdate(filter, update, options) + + /** + * Atomically find a document and update it. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter a document describing the query filter, which may not be null. + * @param update a pipeline describing the update, which may not be null. + * @param options the options to apply to the operation + * @return the document that was updated. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + */ + public fun findOneAndUpdate( + clientSession: ClientSession, + filter: Bson, + update: List, + options: FindOneAndUpdateOptions = FindOneAndUpdateOptions() + ): T? = wrapped.findOneAndUpdate(clientSession.wrapped, filter, update, options) + + /** + * Atomically find a document and replace it. + * + * Use this method to replace a document using the specified replacement argument. To update the document with + * update operators, use the corresponding [findOneAndUpdate] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param filter the query filter to apply the replace operation + * @param replacement the replacement document + * @param options the options to apply to the operation + * @return the document that was replaced. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + */ + public fun findOneAndReplace( + filter: Bson, + replacement: T, + options: FindOneAndReplaceOptions = FindOneAndReplaceOptions() + ): T? = wrapped.findOneAndReplace(filter, replacement, options) + + /** + * Atomically find a document and replace it. + * + * Use this method to replace a document using the specified replacement argument. To update the document with + * update operators, use the corresponding [findOneAndUpdate] method. + * + * Note: Supports retryable writes on MongoDB server versions 3.6 or higher when the retryWrites setting is enabled. + * + * @param clientSession the client session with which to associate this operation + * @param filter the query filter to apply the replace operation + * @param replacement the replacement document + * @param options the options to apply to the operation + * @return the document that was replaced. Depending on the value of the `returnOriginal` property, this will either + * be the document as it was before the update or as it is after the update. If no documents matched the query + * filter, then null will be returned + * @see [Update Command Behaviors](https://www.mongodb.com/docs/manual/reference/command/update/) + */ + public fun findOneAndReplace( + clientSession: ClientSession, + filter: Bson, + replacement: T, + options: FindOneAndReplaceOptions = FindOneAndReplaceOptions() + ): T? = wrapped.findOneAndReplace(clientSession.wrapped, filter, replacement, options) + + /** + * Drops this collection from the Database. + * + * @param options various options for dropping the collection + * @see [Drop Collection](https://www.mongodb.com/docs/manual/reference/command/drop/) + */ + public fun drop(options: DropCollectionOptions = DropCollectionOptions()): Unit = wrapped.drop(options) + + /** + * Drops this collection from the Database. + * + * @param clientSession the client session with which to associate this operation + * @param options various options for dropping the collection + * @see [Drop Collection](https://www.mongodb.com/docs/manual/reference/command/drop/) + */ + public fun drop(clientSession: ClientSession, options: DropCollectionOptions = DropCollectionOptions()): Unit = + wrapped.drop(clientSession.wrapped, options) + + /** + * Create an index with the given keys and options. + * + * @param keys an object describing the index key(s), which may not be null. + * @param options the options for the index + * @return the index name + * @see [Create indexes](https://www.mongodb.com/docs/manual/reference/command/createIndexes/) + */ + public fun createIndex(keys: Bson, options: IndexOptions = IndexOptions()): String = + wrapped.createIndex(keys, options) + + /** + * Create an index with the given keys and options. + * + * @param clientSession the client session with which to associate this operation + * @param keys an object describing the index key(s), which may not be null. + * @param options the options for the index + * @return the index name + * @see [Create indexes](https://www.mongodb.com/docs/manual/reference/command/createIndexes/) + */ + public fun createIndex(clientSession: ClientSession, keys: Bson, options: IndexOptions = IndexOptions()): String = + wrapped.createIndex(clientSession.wrapped, keys, options) + + /** + * Create multiple indexes. + * + * @param indexes the list of indexes + * @param options options to use when creating indexes + * @return the list of index names + * @see [Create indexes](https://www.mongodb.com/docs/manual/reference/command/createIndexes/) + */ + public fun createIndexes( + indexes: List, + options: CreateIndexOptions = CreateIndexOptions() + ): List = wrapped.createIndexes(indexes, options) + + /** + * Create multiple indexes. + * + * @param clientSession the client session with which to associate this operation + * @param indexes the list of indexes + * @param options: options to use when creating indexes + * @return the list of index names + * @see [Create indexes](https://www.mongodb.com/docs/manual/reference/command/createIndexes/) + */ + public fun createIndexes( + clientSession: ClientSession, + indexes: List, + options: CreateIndexOptions = CreateIndexOptions() + ): List = wrapped.createIndexes(clientSession.wrapped, indexes, options) + + /** + * Get all the indexes in this collection. + * + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + @JvmName("listIndexesAsDocument") public fun listIndexes(): ListIndexesIterable = listIndexes() + + /** + * Get all the indexes in this collection. + * + * @param clientSession the client session with which to associate this operation + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + @JvmName("listIndexesAsDocumentWithSession") + public fun listIndexes(clientSession: ClientSession): ListIndexesIterable = + listIndexes(clientSession) + + /** + * Get all the indexes in this collection. + * + * @param R the class to decode each document into + * @param resultClass the target document type of the iterable. + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + public fun listIndexes(resultClass: Class): ListIndexesIterable = + ListIndexesIterable(wrapped.listIndexes(resultClass)) + + /** + * Get all the indexes in this collection. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param resultClass the target document type of the iterable. + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + public fun listIndexes(clientSession: ClientSession, resultClass: Class): ListIndexesIterable = + ListIndexesIterable(wrapped.listIndexes(clientSession.wrapped, resultClass)) + + /** + * Get all the indexes in this collection. + * + * @param R the class to decode each document into + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + public inline fun listIndexes(): ListIndexesIterable = listIndexes(R::class.java) + + /** + * Get all the indexes in this collection. + * + * @param R the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @return the list indexes iterable interface + * @see [List indexes](https://www.mongodb.com/docs/manual/reference/command/listIndexes/) + */ + public inline fun listIndexes(clientSession: ClientSession): ListIndexesIterable = + listIndexes(clientSession, R::class.java) + + /** + * Drops the index given its name. + * + * @param indexName the name of the index to remove + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndex(indexName: String, options: DropIndexOptions = DropIndexOptions()): Unit = + wrapped.dropIndex(indexName, options) + + /** + * Drops the index given the keys used to create it. + * + * @param keys the keys of the index to remove + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndex(keys: Bson, options: DropIndexOptions = DropIndexOptions()): Unit = + wrapped.dropIndex(keys, options) + + /** + * Drops the index given its name. + * + * @param clientSession the client session with which to associate this operation + * @param indexName the name of the index to remove + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndex( + clientSession: ClientSession, + indexName: String, + options: DropIndexOptions = DropIndexOptions() + ): Unit = wrapped.dropIndex(clientSession.wrapped, indexName, options) + + /** + * Drops the index given the keys used to create it. + * + * @param clientSession the client session with which to associate this operation + * @param keys the keys of the index to remove + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndex( + clientSession: ClientSession, + keys: Bson, + options: DropIndexOptions = DropIndexOptions() + ): Unit = wrapped.dropIndex(clientSession.wrapped, keys, options) + + /** + * Drop all the indexes on this collection, except for the default on `_id`. + * + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndexes(options: DropIndexOptions = DropIndexOptions()): Unit = wrapped.dropIndexes(options) + + /** + * Drop all the indexes on this collection, except for the default on `_id`. + * + * @param clientSession the client session with which to associate this operation + * @param options the options to use when dropping indexes + * @see [Drop indexes](https://www.mongodb.com/docs/manual/reference/command/dropIndexes/) + */ + public fun dropIndexes(clientSession: ClientSession, options: DropIndexOptions = DropIndexOptions()): Unit = + wrapped.dropIndexes(clientSession.wrapped, options) + + /** + * Rename the collection with oldCollectionName to the newCollectionName. + * + * @param newCollectionNamespace the name the collection will be renamed to + * @param options the options for renaming a collection + * @throws com.mongodb.MongoServerException if you provide a newCollectionName that is the name of an existing + * collection and dropTarget is false, or if the oldCollectionName is the name of a collection that doesn't exist + * @see [Rename collection](https://www.mongodb.com/docs/manual/reference/command/renameCollection/) + */ + public fun renameCollection( + newCollectionNamespace: MongoNamespace, + options: RenameCollectionOptions = RenameCollectionOptions() + ): Unit = wrapped.renameCollection(newCollectionNamespace, options) + + /** + * Rename the collection with oldCollectionName to the newCollectionName. + * + * @param clientSession the client session with which to associate this operation + * @param newCollectionNamespace the name the collection will be renamed to + * @param options the options for renaming a collection + * @throws com.mongodb.MongoServerException if you provide a newCollectionName that is the name of an existing + * collection and dropTarget is false, or if the oldCollectionName is the name of a collection that doesn't exist + * @see [Rename collection](https://www.mongodb.com/docs/manual/reference/command/renameCollection/) + */ + public fun renameCollection( + clientSession: ClientSession, + newCollectionNamespace: MongoNamespace, + options: RenameCollectionOptions = RenameCollectionOptions() + ): Unit = wrapped.renameCollection(clientSession.wrapped, newCollectionNamespace, options) +} + +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun CreateIndexOptions.maxTime(maxTime: Long): CreateIndexOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun CountOptions.maxTime(maxTime: Long): CountOptions = this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun DropIndexOptions.maxTime(maxTime: Long): DropIndexOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun EstimatedDocumentCountOptions.maxTime(maxTime: Long): EstimatedDocumentCountOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun FindOneAndDeleteOptions.maxTime(maxTime: Long): FindOneAndDeleteOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun FindOneAndReplaceOptions.maxTime(maxTime: Long): FindOneAndReplaceOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * maxTime extension function + * + * @param maxTime time in milliseconds + * @return the options + */ +public fun FindOneAndUpdateOptions.maxTime(maxTime: Long): FindOneAndUpdateOptions = + this.apply { maxTime(maxTime, TimeUnit.MILLISECONDS) } +/** + * expireAfter extension function + * + * @param expireAfter time in seconds + * @return the options + */ +public fun IndexOptions.expireAfter(expireAfter: Long): IndexOptions = + this.apply { expireAfter(expireAfter, TimeUnit.SECONDS) } diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt new file mode 100644 index 00000000000..5c757bf5e65 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt @@ -0,0 +1,135 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ServerAddress +import com.mongodb.ServerCursor +import com.mongodb.client.MongoChangeStreamCursor as JMongoChangeStreamCursor +import com.mongodb.client.MongoCursor as JMongoCursor +import java.io.Closeable +import org.bson.BsonDocument + +/** + * The Mongo Cursor interface implementing the iterator protocol. + * + * An application should ensure that a cursor is closed in all circumstances, e.g. using a `use` statement: + * ``` + * collection.find().cursor().use { c -> + * while (c.hasNext()) { + * println(c.next()) + * } + * } + * ``` + * + * @param T The type of documents the cursor contains + */ +public sealed interface MongoCursor : Iterator, Closeable { + + /** + * Gets the number of results available locally without blocking, which may be 0. + * + * If the cursor is known to be exhausted, returns 0. If the cursor is closed before it's been exhausted, it may + * return a non-zero value. + */ + public val available: Int + + /** + * A special [next] case that returns the next element in the iteration if available or null. + * + * Tailable cursors are an example where this is useful. A call to [tryNext] may return null, ut in the future + * calling [tryNext] would return a new element if a document had been added to the capped collection.

+ * + * @return the next element in the iteration if available or null. + * @see [Tailable Cursor](https://www.mongodb.com/docs/manual/reference/glossary/#term-tailable-cursor) + */ + public fun tryNext(): T? + + /** @return the ServerCursor if available */ + public val serverCursor: ServerCursor? + + /** @return the ServerAddress */ + public val serverAddress: ServerAddress +} + +/** + * The Mongo Cursor interface for change streams implementing the iterator protocol. + * + * An application should ensure that a cursor is closed in all circumstances, e.g. using a `use` statement: + * ``` + * collection.watch().cursor().use { c -> + * while (c.hasNext()) { + * println(c.next()) + * } + * } + * ``` + * + * @param T The type of documents the cursor contains + */ +public sealed interface MongoChangeStreamCursor : MongoCursor { + /** + * Returns the resume token. If a batch has been iterated to the last change stream document in the batch and a + * postBatchResumeToken is included in the document, the postBatchResumeToken will be returned. Otherwise, the + * resume token contained in the last change stream document will be returned. + * + * @return the resume token, which can be null if the cursor has either not been iterated yet, or the cursor is + * closed. + */ + public val resumeToken: BsonDocument? +} + +internal class MongoCursorImpl(private val wrapped: JMongoCursor) : MongoCursor { + + override fun hasNext(): Boolean = wrapped.hasNext() + + override fun next(): T = wrapped.next() + + override fun close(): Unit = wrapped.close() + + override val available: Int + get() = wrapped.available() + + override fun tryNext(): T? = wrapped.tryNext() + + override val serverCursor: ServerCursor? + get() = wrapped.serverCursor + + override val serverAddress: ServerAddress + get() = wrapped.serverAddress +} + +internal class MongoChangeStreamCursorImpl(private val wrapped: JMongoChangeStreamCursor) : + MongoChangeStreamCursor { + + override fun hasNext(): Boolean = wrapped.hasNext() + + override fun next(): T = wrapped.next() + + override fun close(): Unit = wrapped.close() + + override val available: Int + get() = wrapped.available() + + override fun tryNext(): T? = wrapped.tryNext() + + override val serverCursor: ServerCursor? + get() = wrapped.serverCursor + + override val serverAddress: ServerAddress + get() = wrapped.serverAddress + + override val resumeToken: BsonDocument? + get() = wrapped.resumeToken +} diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoDatabase.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoDatabase.kt new file mode 100644 index 00000000000..6ddfbd2c652 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoDatabase.kt @@ -0,0 +1,532 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.client.MongoDatabase as JMongoDatabase +import com.mongodb.client.model.CreateCollectionOptions +import com.mongodb.client.model.CreateViewOptions +import java.util.concurrent.TimeUnit +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.bson.conversions.Bson + +/** The MongoDatabase representation. */ +public class MongoDatabase(private val wrapped: JMongoDatabase) { + + /** The name of the database. */ + public val name: String + get() = wrapped.name + + /** The codec registry for the database. */ + public val codecRegistry: CodecRegistry + get() = wrapped.codecRegistry + + /** The read preference for the database. */ + public val readPreference: ReadPreference + get() = wrapped.readPreference + + /** + * The read concern for the database. + * + * @see [Read Concern](https://www.mongodb.com/docs/manual/reference/readConcern/) + */ + public val readConcern: ReadConcern + get() = wrapped.readConcern + + /** The write concern for the database. */ + public val writeConcern: WriteConcern + get() = wrapped.writeConcern + + /** + * Create a new MongoDatabase instance with a different codec registry. + * + * The [CodecRegistry] configured by this method is effectively treated by the driver as an instance of + * [org.bson.codecs.configuration.CodecProvider], which [CodecRegistry] extends. So there is no benefit to defining + * a class that implements [CodecRegistry]. Rather, an application should always create [CodecRegistry] instances + * using the factory methods in [org.bson.codecs.configuration.CodecRegistries]. + * + * @param newCodecRegistry the new [org.bson.codecs.configuration.CodecRegistry] for the database + * @return a new MongoDatabase instance with the different codec registry + * @see org.bson.codecs.configuration.CodecRegistries + */ + public fun withCodecRegistry(newCodecRegistry: CodecRegistry): MongoDatabase = + MongoDatabase(wrapped.withCodecRegistry(newCodecRegistry)) + + /** + * Create a new MongoDatabase instance with a different read preference. + * + * @param newReadPreference the new [ReadPreference] for the database + * @return a new MongoDatabase instance with the different readPreference + */ + public fun withReadPreference(newReadPreference: ReadPreference): MongoDatabase = + MongoDatabase(wrapped.withReadPreference(newReadPreference)) + + /** + * Create a new MongoDatabase instance with a different read concern. + * + * @param newReadConcern the new [ReadConcern] for the database + * @return a new MongoDatabase instance with the different ReadConcern + * @see [Read Concern](https://www.mongodb.com/docs/manual/reference/readConcern/) + */ + public fun withReadConcern(newReadConcern: ReadConcern): MongoDatabase = + MongoDatabase(wrapped.withReadConcern(newReadConcern)) + + /** + * Create a new MongoDatabase instance with a different write concern. + * + * @param newWriteConcern the new [WriteConcern] for the database + * @return a new MongoDatabase instance with the different writeConcern + */ + public fun withWriteConcern(newWriteConcern: WriteConcern): MongoDatabase = + MongoDatabase(wrapped.withWriteConcern(newWriteConcern)) + + /** + * Gets a collection. + * + * @param T the default class to covert documents returned from the collection into. + * @param collectionName the name of the collection to return + * @param resultClass the target document type for the collection + * @return the collection + */ + public fun getCollection(collectionName: String, resultClass: Class): MongoCollection = + MongoCollection(wrapped.getCollection(collectionName, resultClass)) + + /** + * Gets a collection. + * + * @param T the default class to covert documents returned from the collection into. + * @param collectionName the name of the collection to return + * @return the collection + */ + public inline fun getCollection(collectionName: String): MongoCollection = + getCollection(collectionName, T::class.java) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @return the command result + */ + public fun runCommand(command: Bson, readPreference: ReadPreference = this.readPreference): Document = + runCommand(command, readPreference) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param clientSession the client session with which to associate this operation + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @return the command result + */ + public fun runCommand( + clientSession: ClientSession, + command: Bson, + readPreference: ReadPreference = this.readPreference + ): Document = runCommand(clientSession, command, readPreference) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param T the class to decode each document into + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @param resultClass the target document class + * @return the command result + */ + public fun runCommand( + command: Bson, + readPreference: ReadPreference = this.readPreference, + resultClass: Class + ): T = wrapped.runCommand(command, readPreference, resultClass) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param T the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @param resultClass the target document class + * @return the command result + */ + public fun runCommand( + clientSession: ClientSession, + command: Bson, + readPreference: ReadPreference = this.readPreference, + resultClass: Class + ): T = wrapped.runCommand(clientSession.wrapped, command, readPreference, resultClass) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param T the class to decode each document into + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @return the command result + */ + public inline fun runCommand( + command: Bson, + readPreference: ReadPreference = this.readPreference + ): T = runCommand(command, readPreference, T::class.java) + + /** + * Executes the given command in the context of the current database with the given read preference. + * + * @param T the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param command the command to be run + * @param readPreference the [ReadPreference] to be used when executing the command, defaults to + * [MongoDatabase.readPreference] + * @return the command result + */ + public inline fun runCommand( + clientSession: ClientSession, + command: Bson, + readPreference: ReadPreference = this.readPreference + ): T = runCommand(clientSession, command, readPreference, T::class.java) + + /** + * Drops this database. + * + * @see [Drop database](https://www.mongodb.com/docs/manual/reference/command/dropDatabase/#dbcmd.dropDatabase) + */ + public fun drop(): Unit = wrapped.drop() + + /** + * Drops this database. + * + * @param clientSession the client session with which to associate this operation + * @see [Drop database](https://www.mongodb.com/docs/manual/reference/command/dropDatabase/#dbcmd.dropDatabase) + */ + public fun drop(clientSession: ClientSession): Unit = wrapped.drop(clientSession.wrapped) + + /** + * Gets the names of all the collections in this database. + * + * @return an iterable containing all the names of all the collections in this database + */ + public fun listCollectionNames(): MongoIterable = MongoIterable(wrapped.listCollectionNames()) + + /** + * Gets the names of all the collections in this database. + * + * @param clientSession the client session with which to associate this operation + * @return an iterable containing all the names of all the collections in this database + */ + public fun listCollectionNames(clientSession: ClientSession): MongoIterable = + MongoIterable(wrapped.listCollectionNames(clientSession.wrapped)) + + /** + * Gets all the collections in this database. + * + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + @JvmName("listCollectionsAsDocument") + public fun listCollections(): ListCollectionsIterable = listCollections() + + /** + * Gets all the collections in this database. + * + * @param clientSession the client session with which to associate this operation + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + @JvmName("listCollectionsAsDocumentWithSession") + public fun listCollections(clientSession: ClientSession): ListCollectionsIterable = + listCollections(clientSession) + + /** + * Gets all the collections in this database. + * + * @param T the type of the class to use + * @param resultClass the target document type of the iterable. + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + public fun listCollections(resultClass: Class): ListCollectionsIterable = + ListCollectionsIterable(wrapped.listCollections(resultClass)) + + /** + * Gets all the collections in this database. + * + * @param T the type of the class to use + * @param clientSession the client session with which to associate this operation + * @param resultClass the target document type of the iterable. + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + public fun listCollections( + clientSession: ClientSession, + resultClass: Class + ): ListCollectionsIterable = ListCollectionsIterable(wrapped.listCollections(clientSession.wrapped, resultClass)) + + /** + * Gets all the collections in this database. + * + * @param T the type of the class to use + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + public inline fun listCollections(): ListCollectionsIterable = listCollections(T::class.java) + + /** + * Gets all the collections in this database. + * + * @param clientSession the client session with which to associate this operation + * @param T the type of the class to use + * @return the list collections iterable interface + * @see [listCollections](https://www.mongodb.com/docs/manual/reference/command/listCollections) + */ + public inline fun listCollections(clientSession: ClientSession): ListCollectionsIterable = + listCollections(clientSession, T::class.java) + + /** + * Create a new collection with the selected options + * + * @param collectionName the name for the new collection to create + * @param createCollectionOptions various options for creating the collection + * @see [Create Command](https://www.mongodb.com/docs/manual/reference/command/create) + */ + public fun createCollection( + collectionName: String, + createCollectionOptions: CreateCollectionOptions = CreateCollectionOptions() + ): Unit = wrapped.createCollection(collectionName, createCollectionOptions) + + /** + * Create a new collection with the selected options + * + * @param clientSession the client session with which to associate this operation + * @param collectionName the name for the new collection to create + * @param createCollectionOptions various options for creating the collection + * @see [Create Command](https://www.mongodb.com/docs/manual/reference/command/create) + */ + public fun createCollection( + clientSession: ClientSession, + collectionName: String, + createCollectionOptions: CreateCollectionOptions = CreateCollectionOptions() + ): Unit = wrapped.createCollection(clientSession.wrapped, collectionName, createCollectionOptions) + + /** + * Creates a view with the given name, backing collection/view name, aggregation pipeline, and options that defines + * the view. + * + * @param viewName the name of the view to create + * @param viewOn the backing collection/view for the view + * @param pipeline the pipeline that defines the view + * @param createViewOptions various options for creating the view + * @see [Create Command](https://www.mongodb.com/docs/manual/reference/command/create) + */ + public fun createView( + viewName: String, + viewOn: String, + pipeline: List, + createViewOptions: CreateViewOptions = CreateViewOptions() + ): Unit = wrapped.createView(viewName, viewOn, pipeline, createViewOptions) + + /** + * Creates a view with the given name, backing collection/view name, aggregation pipeline, and options that defines + * the view. + * + * @param clientSession the client session with which to associate this operation + * @param viewName the name of the view to create + * @param viewOn the backing collection/view for the view + * @param pipeline the pipeline that defines the view + * @param createViewOptions various options for creating the view + * @see [Create Command](https://www.mongodb.com/docs/manual/reference/command/create) + */ + public fun createView( + clientSession: ClientSession, + viewName: String, + viewOn: String, + pipeline: List, + createViewOptions: CreateViewOptions = CreateViewOptions() + ): Unit = wrapped.createView(clientSession.wrapped, viewName, viewOn, pipeline, createViewOptions) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + @JvmName("aggregateAsDocument") + public fun aggregate(pipeline: List): AggregateIterable = aggregate(pipeline) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + @JvmName("aggregateAsDocumentWithSession") + public fun aggregate(clientSession: ClientSession, pipeline: List): AggregateIterable = + aggregate(clientSession, pipeline) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param T the class to decode each document into + * @param pipeline the aggregation pipeline + * @param resultClass the target document type of the iterable. + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + public fun aggregate(pipeline: List, resultClass: Class): AggregateIterable = + AggregateIterable(wrapped.aggregate(pipeline, resultClass)) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param T the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @param resultClass the target document type of the iterable. + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + public fun aggregate( + clientSession: ClientSession, + pipeline: List, + resultClass: Class + ): AggregateIterable = AggregateIterable(wrapped.aggregate(clientSession.wrapped, pipeline, resultClass)) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param T the class to decode each document into + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + public inline fun aggregate(pipeline: List): AggregateIterable = + aggregate(pipeline, T::class.java) + + /** + * Runs an aggregation framework pipeline on the database for pipeline stages that do not require an underlying + * collection, such as `$currentOp` and `$listLocalSessions`. + * + * @param T the class to decode each document into + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline + * @return an iterable containing the result of the aggregation operation + * @see [Aggregate Command](https://www.mongodb.com/docs/manual/reference/command/aggregate/#dbcmd.aggregate) + */ + public inline fun aggregate( + clientSession: ClientSession, + pipeline: List + ): AggregateIterable = aggregate(clientSession, pipeline, T::class.java) + + /** + * Creates a change stream for this database. + * + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocument") + public fun watch(pipeline: List = emptyList()): ChangeStreamIterable = watch(pipeline) + + /** + * Creates a change stream for this database. + * + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + @JvmName("watchAsDocumentWithSession") + public fun watch(clientSession: ClientSession, pipeline: List = emptyList()): ChangeStreamIterable = + watch(clientSession, pipeline) + + /** + * Creates a change stream for this database. + * + * @param T the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch(pipeline: List = emptyList(), resultClass: Class): ChangeStreamIterable = + ChangeStreamIterable(wrapped.watch(pipeline, resultClass)) + + /** + * Creates a change stream for this database. + * + * @param T the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @param resultClass the target document type of the iterable. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public fun watch( + clientSession: ClientSession, + pipeline: List = emptyList(), + resultClass: Class + ): ChangeStreamIterable = ChangeStreamIterable(wrapped.watch(clientSession.wrapped, pipeline, resultClass)) + + /** + * Creates a change stream for this database. + * + * @param T the target document type of the iterable. + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch(pipeline: List = emptyList()): ChangeStreamIterable = + watch(pipeline, T::class.java) + + /** + * Creates a change stream for this database. + * + * @param T the target document type of the iterable. + * @param clientSession the client session with which to associate this operation + * @param pipeline the aggregation pipeline to apply to the change stream, defaults to an empty pipeline. + * @return the change stream iterable + * @see [Change Streams](https://dochub.mongodb.org/changestreams] + */ + public inline fun watch( + clientSession: ClientSession, + pipeline: List = emptyList() + ): ChangeStreamIterable = watch(clientSession, pipeline, T::class.java) +} + +/** + * expireAfter extension function + * + * @param maxTime time in seconds + * @return the options + */ +public fun CreateCollectionOptions.expireAfter(maxTime: Long): CreateCollectionOptions = + this.apply { expireAfter(maxTime, TimeUnit.SECONDS) } diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt new file mode 100644 index 00000000000..fffcea2ce76 --- /dev/null +++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt @@ -0,0 +1,90 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.MongoClientException +import com.mongodb.client.MongoIterable as JMongoIterable + +/** + * The MongoIterable is the results from an operation, such as a query. + * + * @param T The type that this iterable will decode documents to. + */ +public open class MongoIterable(private val delegate: JMongoIterable) { + + /** + * Returns a cursor used for iterating over elements of type `T. The cursor is primarily used for change streams. + * + * Note: Care must be taken to ensure the returned [MongoCursor] is closed after use. + * + * @return a cursor + */ + public open fun cursor(): MongoCursor = MongoCursorImpl(delegate.cursor()) + + /** @return the first item or null */ + public fun firstOrNull(): T? = delegate.first() + + /** @return the first item or throw a [MongoClientException] if no results are available */ + public fun first(): T = firstOrNull() ?: throw MongoClientException("No results available") + + /** + * Sets the number of documents to return per batch. + * + * @param batchSize the batch size + * @return this + * @see [Batch Size](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/#cursor.batchSize) + */ + public open fun batchSize(batchSize: Int): MongoIterable = apply { delegate.batchSize(batchSize) } + + /** + * Creates a new cursor and treats it as a [Sequence], invokes the given [consumer] function and closes the cursor + * down correctly whether an exception is thrown or not. + * + * This allows the [MongoIterable] to be safely treated as a lazily evaluated sequence. + * + * Note: Sequence filters and aggregations have a performance cost, it is best to use server side filters and + * aggregations where available. + * + * @param R the result type + * @param consumer the sequence consumer + * @return the result of the consumer + */ + public fun use(consumer: (Sequence) -> R): R = cursor().use { consumer.invoke(it.asSequence()) } + + /** + * Maps this iterable from the source document type to the target document type. + * + * @param R the result document type + * @param transform a function that maps from the source to the target document type + * @return an iterable which maps T to U + */ + public fun map(transform: (T) -> R): MongoIterable = MongoIterable(delegate.map(transform)) + + /** Performs the given [action] on each element and safely closes the cursor. */ + public fun forEach(action: (T) -> Unit): Unit = use { it.forEach(action) } + + /** + * Appends all elements to the given [destination] collection. + * + * @param C the type of the collection + * @param destination the destination collection + * @return the collection + */ + public fun > toCollection(destination: C): C = use { it.toCollection(destination) } + + /** @return a [List] containing all elements. */ + public fun toList(): List = toCollection(ArrayList()).toList() +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/AggregateIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/AggregateIterableTest.kt new file mode 100644 index 00000000000..ce1ed2dea47 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/AggregateIterableTest.kt @@ -0,0 +1,105 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ExplainVerbosity +import com.mongodb.client.AggregateIterable as JAggregateIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class AggregateIterableTest { + + @Test + fun shouldHaveTheSameMethods() { + val jAggregateIterableFunctions = JAggregateIterable::class.declaredFunctions.map { it.name }.toSet() + val kAggregateIterableFunctions = AggregateIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jAggregateIterableFunctions, kAggregateIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JAggregateIterable = mock() + val iterable = AggregateIterable(wrapped) + + val batchSize = 10 + val bson = BsonDocument() + val bsonComment = BsonString("a comment") + val collation = Collation.builder().locale("en").build() + val comment = "comment" + val hint = Document("h", 1) + val hintString = "hintString" + val verbosity = ExplainVerbosity.QUERY_PLANNER + + whenever(wrapped.explain(Document::class.java)).doReturn(mock()) + whenever(wrapped.explain(Document::class.java, verbosity)).doReturn(mock()) + whenever(wrapped.explain(BsonDocument::class.java, verbosity)).doReturn(mock()) + + iterable.allowDiskUse(true) + iterable.batchSize(batchSize) + iterable.bypassDocumentValidation(true) + iterable.collation(collation) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.explain() + iterable.explain(verbosity) + iterable.explain(Document::class.java) + iterable.explain(BsonDocument::class.java, verbosity) + iterable.explain() + iterable.explain(verbosity) + iterable.hint(hint) + iterable.hintString(hintString) + iterable.let(bson) + iterable.maxAwaitTime(1) + iterable.maxAwaitTime(1, TimeUnit.SECONDS) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + + verify(wrapped).allowDiskUse(true) + verify(wrapped).batchSize(batchSize) + verify(wrapped).bypassDocumentValidation(true) + verify(wrapped).collation(collation) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped, times(3)).explain(Document::class.java) + verify(wrapped, times(1)).explain(Document::class.java, verbosity) + verify(wrapped, times(2)).explain(BsonDocument::class.java, verbosity) + verify(wrapped).hint(hint) + verify(wrapped).hintString(hintString) + verify(wrapped).maxAwaitTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxAwaitTime(1, TimeUnit.SECONDS) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + verify(wrapped).let(bson) + + iterable.toCollection() + verify(wrapped).toCollection() + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ChangeStreamIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ChangeStreamIterableTest.kt new file mode 100644 index 00000000000..53d56485c21 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ChangeStreamIterableTest.kt @@ -0,0 +1,93 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ChangeStreamIterable as JChangeStreamIterable +import com.mongodb.client.model.Collation +import com.mongodb.client.model.changestream.FullDocument +import com.mongodb.client.model.changestream.FullDocumentBeforeChange +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.BsonTimestamp +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class ChangeStreamIterableTest { + + @Test + fun shouldHaveTheSameMethods() { + val jChangeStreamIterableFunctions = JChangeStreamIterable::class.declaredFunctions.map { it.name }.toSet() + val kChangeStreamIterableFunctions = ChangeStreamIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jChangeStreamIterableFunctions, kChangeStreamIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JChangeStreamIterable = mock() + val iterable = ChangeStreamIterable(wrapped) + + val batchSize = 10 + val bsonComment = BsonString("a comment") + val collation = Collation.builder().locale("en").build() + val comment = "comment" + val operationTime = BsonTimestamp(1) + val resumeToken = BsonDocument() + + whenever(wrapped.withDocumentClass(BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.cursor()).doReturn(mock()) + + iterable.batchSize(batchSize) + iterable.collation(collation) + iterable.comment(comment) + iterable.comment(bsonComment) + iterable.cursor() + iterable.fullDocument(FullDocument.UPDATE_LOOKUP) + iterable.fullDocumentBeforeChange(FullDocumentBeforeChange.REQUIRED) + iterable.maxAwaitTime(1) + iterable.maxAwaitTime(1, TimeUnit.SECONDS) + iterable.resumeAfter(resumeToken) + iterable.showExpandedEvents(true) + iterable.startAfter(resumeToken) + iterable.startAtOperationTime(operationTime) + iterable.withDocumentClass() + + verify(wrapped).batchSize(batchSize) + verify(wrapped).collation(collation) + verify(wrapped).comment(comment) + verify(wrapped).comment(bsonComment) + verify(wrapped).cursor() + verify(wrapped).fullDocument(FullDocument.UPDATE_LOOKUP) + verify(wrapped).fullDocumentBeforeChange(FullDocumentBeforeChange.REQUIRED) + verify(wrapped).maxAwaitTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxAwaitTime(1, TimeUnit.SECONDS) + verify(wrapped).resumeAfter(resumeToken) + verify(wrapped).showExpandedEvents(true) + verify(wrapped).startAfter(resumeToken) + verify(wrapped).startAtOperationTime(operationTime) + verify(wrapped).withDocumentClass(BsonDocument::class.java) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ClientSessionTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ClientSessionTest.kt new file mode 100644 index 00000000000..63309969104 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ClientSessionTest.kt @@ -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 com.mongodb.kotlin.client + +import com.mongodb.ClientSessionOptions +import com.mongodb.TransactionOptions +import com.mongodb.client.ClientSession as JClientSession +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.functions +import kotlin.test.assertEquals +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class ClientSessionTest { + + @Test + fun shouldHaveTheSameMethods() { + val internalFunctions = + setOf( + "advanceClusterTime", + "advanceOperationTime", + "clearTransactionContext", + "getClusterTime", + "getOperationTime", + "getOriginator", + "getPinnedServerAddress", + "getRecoveryToken", + "getServerSession", + "getSnapshotTimestamp", + "getTransactionContext", + "notifyMessageSent", + "notifyOperationInitiated", + "setRecoveryToken", + "setSnapshotTimestamp", + "setTransactionContext") + + val jClientSessionFunctions = JClientSession::class.functions.map { it.name }.toSet() - internalFunctions + val kClientSessionFunctions = + ClientSession::class.functions.map { it.name }.toSet() + + ClientSession::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { + if (it.name.startsWith("is") || it.name.startsWith("has")) it.name + else "get${it.name.replaceFirstChar { c -> c.uppercaseChar()}}" + } + + assertEquals(jClientSessionFunctions, kClientSessionFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JClientSession = mock() + val session = ClientSession(wrapped) + + val transactionOptions = TransactionOptions.builder().maxCommitTime(10).build() + + whenever(wrapped.options).doReturn(ClientSessionOptions.builder().build()) + whenever(wrapped.isCausallyConsistent).doReturn(true) + whenever(wrapped.transactionOptions).doReturn(transactionOptions) + + session.options + session.isCausallyConsistent + session.startTransaction() + session.startTransaction(transactionOptions) + session.transactionOptions + + verify(wrapped).options + verify(wrapped).isCausallyConsistent + verify(wrapped).startTransaction() + verify(wrapped).startTransaction(transactionOptions) + verify(wrapped).transactionOptions + + session.abortTransaction() + session.commitTransaction() + + verify(wrapped).abortTransaction() + verify(wrapped).commitTransaction() + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/DistinctIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/DistinctIterableTest.kt new file mode 100644 index 00000000000..c9fc79e8128 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/DistinctIterableTest.kt @@ -0,0 +1,69 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.DistinctIterable as JDistinctIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions + +class DistinctIterableTest { + @Test + fun shouldHaveTheSameMethods() { + val jDistinctIterableFunctions = JDistinctIterable::class.declaredFunctions.map { it.name }.toSet() + val kDistinctIterableFunctions = DistinctIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jDistinctIterableFunctions, kDistinctIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JDistinctIterable = mock() + val iterable = DistinctIterable(wrapped) + + val batchSize = 10 + val bsonComment = BsonString("a comment") + val collation = Collation.builder().locale("en").build() + val comment = "comment" + val filter = BsonDocument() + + iterable.batchSize(batchSize) + iterable.collation(collation) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.filter(filter) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + + verify(wrapped).batchSize(batchSize) + verify(wrapped).collation(collation) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped).filter(filter) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt new file mode 100644 index 00000000000..ad8571e5696 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ExtensionMethodsTest.kt @@ -0,0 +1,56 @@ +/* + * 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 com.mongodb.kotlin.client + +import io.github.classgraph.ClassGraph +import kotlin.test.assertEquals +import org.junit.jupiter.api.Test + +class ExtensionMethodsTest { + + @Test + fun shouldHaveTimeUnitExtensionsMethodsForOptionsClasses() { + + val extensionsAddedForClasses = + setOf( + "CountOptions", + "CreateCollectionOptions", + "CreateIndexOptions", + "DropIndexOptions", + "EstimatedDocumentCountOptions", + "FindOneAndDeleteOptions", + "FindOneAndReplaceOptions", + "FindOneAndUpdateOptions", + "IndexOptions", + "TransactionOptions") + + ClassGraph().enableClassInfo().enableMethodInfo().acceptPackages("com.mongodb").scan().use { scanResult -> + val optionsClassesWithTimeUnit = + scanResult.allClasses + .filter { !it.packageName.contains("internal") } + .filter { it.simpleName.endsWith("Options") } + .filter { + it.methodInfo.any { m -> + m.parameterInfo.any { p -> p.typeDescriptor.toStringWithSimpleNames().equals("TimeUnit") } + } + } + .map { c -> c.simpleName } + .toSet() + + assertEquals(extensionsAddedForClasses, optionsClassesWithTimeUnit) + } + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/FindIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/FindIterableTest.kt new file mode 100644 index 00000000000..c84176a0cbf --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/FindIterableTest.kt @@ -0,0 +1,128 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.CursorType +import com.mongodb.ExplainVerbosity +import com.mongodb.client.FindIterable as JFindIterable +import com.mongodb.client.model.Collation +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class FindIterableTest { + @Test + fun shouldHaveTheSameMethods() { + val jFindIterableFunctions = JFindIterable::class.declaredFunctions.map { it.name }.toSet() + val kFindIterableFunctions = FindIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jFindIterableFunctions, kFindIterableFunctions) + } + + @Suppress("DEPRECATION") + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JFindIterable = mock() + val iterable = FindIterable(wrapped) + + val batchSize = 10 + val bson = BsonDocument() + val bsonComment = BsonString("a comment") + val collation = Collation.builder().locale("en").build() + val comment = "comment" + val filter = BsonDocument() + val hint = Document("h", 1) + val hintString = "hintString" + val verbosity = ExplainVerbosity.QUERY_PLANNER + + whenever(wrapped.explain(Document::class.java)).doReturn(mock()) + whenever(wrapped.explain(Document::class.java, verbosity)).doReturn(mock()) + whenever(wrapped.explain(BsonDocument::class.java, verbosity)).doReturn(mock()) + + iterable.allowDiskUse(true) + iterable.batchSize(batchSize) + iterable.collation(collation) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.cursorType(CursorType.NonTailable) + iterable.explain() + iterable.explain(verbosity) + iterable.explain(Document::class.java) + iterable.explain(BsonDocument::class.java, verbosity) + iterable.explain() + iterable.explain(verbosity) + iterable.filter(filter) + iterable.hint(hint) + iterable.hintString(hintString) + iterable.let(bson) + iterable.limit(1) + iterable.max(bson) + iterable.maxAwaitTime(1) + iterable.maxAwaitTime(1, TimeUnit.SECONDS) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + iterable.min(bson) + iterable.oplogReplay(true) + iterable.noCursorTimeout(true) + iterable.partial(true) + iterable.projection(bson) + iterable.returnKey(true) + iterable.showRecordId(true) + iterable.skip(1) + iterable.sort(bson) + + verify(wrapped).allowDiskUse(true) + verify(wrapped).batchSize(batchSize) + verify(wrapped).collation(collation) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped).cursorType(CursorType.NonTailable) + verify(wrapped, times(3)).explain(Document::class.java) + verify(wrapped, times(1)).explain(Document::class.java, verbosity) + verify(wrapped, times(2)).explain(BsonDocument::class.java, verbosity) + verify(wrapped).filter(filter) + verify(wrapped).hint(hint) + verify(wrapped).hintString(hintString) + verify(wrapped).let(bson) + verify(wrapped).limit(1) + verify(wrapped).max(bson) + verify(wrapped).maxAwaitTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxAwaitTime(1, TimeUnit.SECONDS) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + verify(wrapped).min(bson) + verify(wrapped).oplogReplay(true) + verify(wrapped).noCursorTimeout(true) + verify(wrapped).partial(true) + verify(wrapped).projection(bson) + verify(wrapped).returnKey(true) + verify(wrapped).showRecordId(true) + verify(wrapped).skip(1) + verify(wrapped).sort(bson) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListCollectionsIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListCollectionsIterableTest.kt new file mode 100644 index 00000000000..b0c23b331e4 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListCollectionsIterableTest.kt @@ -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 com.mongodb.kotlin.client + +import com.mongodb.client.ListCollectionsIterable as JListCollectionsIterable +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions + +class ListCollectionsIterableTest { + @Test + fun shouldHaveTheSameMethods() { + val jListCollectionsIterableFunctions = + JListCollectionsIterable::class.declaredFunctions.map { it.name }.toSet() + val kListCollectionsIterableFunctions = ListCollectionsIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jListCollectionsIterableFunctions, kListCollectionsIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JListCollectionsIterable = mock() + val iterable = ListCollectionsIterable(wrapped) + + val batchSize = 10 + val bsonComment = BsonString("a comment") + val comment = "comment" + val filter = BsonDocument() + + iterable.batchSize(batchSize) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.filter(filter) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + + verify(wrapped).batchSize(batchSize) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped).filter(filter) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListDatabasesIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListDatabasesIterableTest.kt new file mode 100644 index 00000000000..c10ef133c1d --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListDatabasesIterableTest.kt @@ -0,0 +1,69 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ListDatabasesIterable as JListDatabasesIterable +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions + +class ListDatabasesIterableTest { + @Test + fun shouldHaveTheSameMethods() { + val jListDatabasesIterableFunctions = JListDatabasesIterable::class.declaredFunctions.map { it.name }.toSet() + val kListDatabasesIterableFunctions = ListDatabasesIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jListDatabasesIterableFunctions, kListDatabasesIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JListDatabasesIterable = mock() + val iterable = ListDatabasesIterable(wrapped) + + val batchSize = 10 + val bsonComment = BsonString("a comment") + val comment = "comment" + val filter = BsonDocument() + + iterable.authorizedDatabasesOnly(true) + iterable.batchSize(batchSize) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.filter(filter) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + iterable.nameOnly(true) + + verify(wrapped).authorizedDatabasesOnly(true) + verify(wrapped).batchSize(batchSize) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped).filter(filter) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + verify(wrapped).nameOnly(true) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListIndexesIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListIndexesIterableTest.kt new file mode 100644 index 00000000000..70c799eeee4 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/ListIndexesIterableTest.kt @@ -0,0 +1,61 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.client.ListIndexesIterable as JListIndexesIterable +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.test.assertEquals +import org.bson.BsonString +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions + +class ListIndexesIterableTest { + @Test + fun shouldHaveTheSameMethods() { + val jListIndexesIterableFunctions = JListIndexesIterable::class.declaredFunctions.map { it.name }.toSet() + val kListIndexesIterableFunctions = ListIndexesIterable::class.declaredFunctions.map { it.name }.toSet() + + assertEquals(jListIndexesIterableFunctions, kListIndexesIterableFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JListIndexesIterable = mock() + val iterable = ListIndexesIterable(wrapped) + + val batchSize = 10 + val bsonComment = BsonString("a comment") + val comment = "comment" + + iterable.batchSize(batchSize) + iterable.comment(bsonComment) + iterable.comment(comment) + iterable.maxTime(1) + iterable.maxTime(1, TimeUnit.SECONDS) + + verify(wrapped).batchSize(batchSize) + verify(wrapped).comment(bsonComment) + verify(wrapped).comment(comment) + verify(wrapped).maxTime(1, TimeUnit.MILLISECONDS) + verify(wrapped).maxTime(1, TimeUnit.SECONDS) + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MockitoHelper.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MockitoHelper.kt new file mode 100644 index 00000000000..838a26f5dff --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MockitoHelper.kt @@ -0,0 +1,53 @@ +/* + * 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 com.mongodb.kotlin.client + +import org.assertj.core.api.Assertions.assertThat +import org.mockito.ArgumentMatcher +import org.mockito.ArgumentMatchers.argThat + +/** Mockito test helper object */ +object MockitoHelper { + + /** + * Deep reflection comparison for complex nested objects + * + * The usecase is to reflect complex objects that don't have an equals method and contain nested complex properties + * that also do not contain equals values + * + * Example: + * ``` + * verify(wrapped).createCollection(eq(name), deepRefEq(defaultOptions)) + * ``` + * + * @param T the type of the value + * @param value the value + * @return the value + * @see [org.mockito.kotlin.refEq] + */ + fun deepRefEq(value: T): T = argThat(DeepReflectionEqMatcher(value)) + + private class DeepReflectionEqMatcher(private val expected: T) : ArgumentMatcher { + override fun matches(argument: T): Boolean { + return try { + assertThat(argument).usingRecursiveComparison().isEqualTo(expected) + true + } catch (e: Throwable) { + false + } + } + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoChangeStreamCursorTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoChangeStreamCursorTest.kt new file mode 100644 index 00000000000..7bd6008df7c --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoChangeStreamCursorTest.kt @@ -0,0 +1,72 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ServerAddress +import com.mongodb.ServerCursor +import com.mongodb.client.MongoChangeStreamCursor as JMongoChangeStreamCursor +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoChangeStreamCursorTest { + @Test + fun shouldHaveTheSameMethods() { + val jMongoChangeStreamCursorFunctions = + JMongoChangeStreamCursor::class.declaredFunctions.map { it.name }.toSet() + val kMongoChangeStreamCursorFunctions = + MongoChangeStreamCursor::class.declaredFunctions.map { it.name }.toSet() + + MongoChangeStreamCursor::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { "get${it.name.replaceFirstChar{c -> c.uppercaseChar() }}" } + + assertEquals(jMongoChangeStreamCursorFunctions, kMongoChangeStreamCursorFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JMongoChangeStreamCursor = mock() + val cursor = MongoChangeStreamCursorImpl(wrapped) + + whenever(wrapped.resumeToken).doReturn(mock()) + whenever(wrapped.serverCursor).doReturn(ServerCursor(1, ServerAddress())) + whenever(wrapped.serverAddress).doReturn(mock()) + + cursor.serverCursor + cursor.serverAddress + cursor.hasNext() + cursor.tryNext() + cursor.available + cursor.resumeToken + + verify(wrapped).serverCursor + verify(wrapped).serverAddress + verify(wrapped).hasNext() + verify(wrapped).tryNext() + verify(wrapped).available() + verify(wrapped).resumeToken + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt new file mode 100644 index 00000000000..0999e77080e --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoClientTest.kt @@ -0,0 +1,171 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ClientSessionOptions +import com.mongodb.client.MongoClient as JMongoClient +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.Mock +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.refEq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoClientTest { + + @Mock val wrapped: JMongoClient = mock() + @Mock val clientSession: ClientSession = ClientSession(mock()) + + @Test + fun shouldHaveTheSameMethods() { + val jMongoClientFunctions = JMongoClient::class.declaredFunctions.map { it.name }.toSet() + val kMongoClientFunctions = + MongoClient::class.declaredFunctions.map { it.name }.toSet() + + MongoClient::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { "get${it.name.replaceFirstChar{c -> c.uppercaseChar() }}" } + + assertEquals(jMongoClientFunctions, kMongoClientFunctions) + } + + @Test + fun shouldCallTheUnderlyingClose() { + val mongoClient = MongoClient(wrapped) + mongoClient.close() + + verify(wrapped).close() + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingClusterDescription() { + val mongoClient = MongoClient(wrapped) + whenever(wrapped.clusterDescription).doReturn(mock()) + + mongoClient.clusterDescription + + verify(wrapped).clusterDescription + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetDatabase() { + val mongoClient = MongoClient(wrapped) + whenever(wrapped.getDatabase(any())).doReturn(mock()) + + mongoClient.getDatabase("dbName") + verify(wrapped).getDatabase("dbName") + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shoulCallTheUnderlyingStartSession() { + val mongoClient = MongoClient(wrapped) + val defaultOptions = ClientSessionOptions.builder().build() + val options = ClientSessionOptions.builder().causallyConsistent(true).build() + + whenever(wrapped.startSession(refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.startSession(options)).doReturn(mock()) + + mongoClient.startSession() + mongoClient.startSession(options) + + verify(wrapped).startSession(refEq(defaultOptions)) + verify(wrapped).startSession(options) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingListDatabaseNames() { + val mongoClient = MongoClient(wrapped) + whenever(wrapped.listDatabaseNames()).doReturn(mock()) + whenever(wrapped.listDatabaseNames(any())).doReturn(mock()) + + mongoClient.listDatabaseNames() + mongoClient.listDatabaseNames(clientSession) + + verify(wrapped).listDatabaseNames() + verify(wrapped).listDatabaseNames(clientSession.wrapped) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingListDatabases() { + val mongoClient = MongoClient(wrapped) + whenever(wrapped.listDatabases(Document::class.java)).doReturn(mock()) + whenever(wrapped.listDatabases(clientSession.wrapped, Document::class.java)).doReturn(mock()) + whenever(wrapped.listDatabases(clientSession.wrapped, BsonDocument::class.java)).doReturn(mock()) + + mongoClient.listDatabases() + mongoClient.listDatabases(clientSession) + mongoClient.listDatabases(Document::class.java) + mongoClient.listDatabases(clientSession, BsonDocument::class.java) + mongoClient.listDatabases() + mongoClient.listDatabases(clientSession) + + verify(wrapped, times(3)).listDatabases(Document::class.java) + verify(wrapped, times(1)).listDatabases(clientSession.wrapped, Document::class.java) + verify(wrapped, times(2)).listDatabases(clientSession.wrapped, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWatch() { + val mongoClient = MongoClient(wrapped) + val pipeline = listOf(Document(mapOf("a" to 1))) + + whenever(wrapped.watch(emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, BsonDocument::class.java)).doReturn(mock()) + + mongoClient.watch() + mongoClient.watch(pipeline) + mongoClient.watch(clientSession) + mongoClient.watch(clientSession, pipeline) + + mongoClient.watch(resultClass = Document::class.java) + mongoClient.watch(pipeline, BsonDocument::class.java) + mongoClient.watch(clientSession = clientSession, resultClass = Document::class.java) + mongoClient.watch(clientSession, pipeline, BsonDocument::class.java) + + mongoClient.watch() + mongoClient.watch(pipeline) + mongoClient.watch(clientSession) + mongoClient.watch(clientSession, pipeline) + + verify(wrapped, times(3)).watch(emptyList(), Document::class.java) + verify(wrapped, times(1)).watch(pipeline, Document::class.java) + verify(wrapped, times(3)).watch(clientSession.wrapped, emptyList(), Document::class.java) + verify(wrapped, times(1)).watch(clientSession.wrapped, pipeline, Document::class.java) + verify(wrapped, times(2)).watch(pipeline, BsonDocument::class.java) + verify(wrapped, times(2)).watch(clientSession.wrapped, pipeline, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCollectionTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCollectionTest.kt new file mode 100644 index 00000000000..d458c9302ce --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCollectionTest.kt @@ -0,0 +1,882 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.CreateIndexCommitQuorum +import com.mongodb.MongoNamespace +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.client.MongoCollection as JMongoCollection +import com.mongodb.client.model.BulkWriteOptions +import com.mongodb.client.model.CountOptions +import com.mongodb.client.model.CreateIndexOptions +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 +import com.mongodb.client.model.FindOneAndReplaceOptions +import com.mongodb.client.model.FindOneAndUpdateOptions +import com.mongodb.client.model.IndexModel +import com.mongodb.client.model.IndexOptions +import com.mongodb.client.model.InsertManyOptions +import com.mongodb.client.model.InsertOneModel +import com.mongodb.client.model.InsertOneOptions +import com.mongodb.client.model.RenameCollectionOptions +import com.mongodb.client.model.ReplaceOptions +import com.mongodb.client.model.UpdateOptions +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.junit.jupiter.api.Test +import org.mockito.Mock +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.refEq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoCollectionTest { + + @Mock val wrapped: JMongoCollection = mock() + @Mock val clientSession: ClientSession = ClientSession(mock()) + + private val defaultFilter = BsonDocument() + private val filter = Document("a", 1) + private val pipeline = listOf(Document(mapOf("a" to 1))) + + @Test + fun shouldHaveTheSameMethods() { + val jMongoCollectionFunctions = JMongoCollection::class.declaredFunctions.map { it.name }.toSet() - "mapReduce" + val kMongoCollectionFunctions = + MongoCollection::class.declaredFunctions.map { it.name }.toSet() + + MongoCollection::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { "get${it.name.replaceFirstChar { c -> c.uppercaseChar() }}" } + + assertEquals(jMongoCollectionFunctions, kMongoCollectionFunctions) + } + + @Test + fun shouldCallTheUnderlyingGetDocumentClass() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.documentClass).doReturn(Document::class.java) + + mongoCollection.documentClass + verify(wrapped).documentClass + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetNamespace() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.namespace).doReturn(MongoNamespace("a.b")) + + mongoCollection.namespace + verify(wrapped).namespace + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetCodecRegistry() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.codecRegistry).doReturn(mock()) + + mongoCollection.codecRegistry + verify(wrapped).codecRegistry + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetReadPreference() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.readPreference).doReturn(mock()) + + mongoCollection.readPreference + verify(wrapped).readPreference + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetReadConcern() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.readConcern).doReturn(ReadConcern.DEFAULT) + + mongoCollection.readConcern + verify(wrapped).readConcern + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetWriteConcern() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.writeConcern).doReturn(mock()) + + mongoCollection.writeConcern + verify(wrapped).writeConcern + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithDocumentClass() { + val mongoCollection = MongoCollection(wrapped) + whenever(wrapped.withDocumentClass(BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.withDocumentClass() + verify(wrapped).withDocumentClass(BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithCodecRegistry() { + val mongoCollection = MongoCollection(wrapped) + val codecRegistry = mock() + whenever(wrapped.withCodecRegistry(codecRegistry)).doReturn(mock()) + + mongoCollection.withCodecRegistry(codecRegistry) + verify(wrapped).withCodecRegistry(codecRegistry) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithReadPreference() { + val mongoCollection = MongoCollection(wrapped) + val readPreference = ReadPreference.primaryPreferred() + whenever(wrapped.withReadPreference(readPreference)).doReturn(mock()) + + mongoCollection.withReadPreference(readPreference) + verify(wrapped).withReadPreference(readPreference) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithReadConcern() { + val mongoCollection = MongoCollection(wrapped) + val readConcern = ReadConcern.AVAILABLE + whenever(wrapped.withReadConcern(readConcern)).doReturn(mock()) + + mongoCollection.withReadConcern(readConcern) + verify(wrapped).withReadConcern(readConcern) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithWriteConcern() { + val mongoCollection = MongoCollection(wrapped) + val writeConcern = WriteConcern.MAJORITY + whenever(wrapped.withWriteConcern(writeConcern)).doReturn(mock()) + + mongoCollection.withWriteConcern(writeConcern) + verify(wrapped).withWriteConcern(writeConcern) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingCountDocuments() { + val mongoCollection = MongoCollection(wrapped) + + val defaultOptions = CountOptions() + + val options = CountOptions().comment("comment") + + whenever(wrapped.countDocuments(eq(defaultFilter), refEq(defaultOptions))).doReturn(1) + whenever(wrapped.countDocuments(eq(filter), refEq(defaultOptions))).doReturn(2) + whenever(wrapped.countDocuments(eq(filter), eq(options))).doReturn(3) + whenever(wrapped.countDocuments(eq(clientSession.wrapped), eq(defaultFilter), refEq(defaultOptions))) + .doReturn(4) + whenever(wrapped.countDocuments(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions))).doReturn(5) + whenever(wrapped.countDocuments(eq(clientSession.wrapped), eq(filter), eq(options))).doReturn(6) + + assertEquals(1, mongoCollection.countDocuments()) + assertEquals(2, mongoCollection.countDocuments(filter)) + assertEquals(3, mongoCollection.countDocuments(filter, options)) + assertEquals(4, mongoCollection.countDocuments(clientSession)) + assertEquals(5, mongoCollection.countDocuments(clientSession, filter)) + assertEquals(6, mongoCollection.countDocuments(clientSession, filter, options)) + + verify(wrapped).countDocuments(eq(defaultFilter), refEq(defaultOptions)) + verify(wrapped).countDocuments(eq(filter), refEq(defaultOptions)) + verify(wrapped).countDocuments(eq(filter), eq(options)) + verify(wrapped).countDocuments(eq(clientSession.wrapped), eq(defaultFilter), refEq(defaultOptions)) + verify(wrapped).countDocuments(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions)) + verify(wrapped).countDocuments(eq(clientSession.wrapped), eq(filter), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingEstimatedDocumentCount() { + val mongoCollection = MongoCollection(wrapped) + val defaultOptions = EstimatedDocumentCountOptions() + val options = EstimatedDocumentCountOptions().comment("comment") + + whenever(wrapped.estimatedDocumentCount(refEq(defaultOptions))).doReturn(1) + whenever(wrapped.estimatedDocumentCount(options)).doReturn(2) + + assertEquals(1, mongoCollection.estimatedDocumentCount()) + assertEquals(2, mongoCollection.estimatedDocumentCount(options)) + + verify(wrapped).estimatedDocumentCount(refEq(defaultOptions)) + verify(wrapped).estimatedDocumentCount(options) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDistinct() { + val mongoCollection = MongoCollection(wrapped) + val fieldName = "fieldName" + + whenever(wrapped.distinct(fieldName, defaultFilter, Document::class.java)).doReturn(mock()) + whenever(wrapped.distinct(fieldName, filter, Document::class.java)).doReturn(mock()) + whenever(wrapped.distinct(clientSession.wrapped, fieldName, defaultFilter, Document::class.java)) + .doReturn(mock()) + whenever(wrapped.distinct(clientSession.wrapped, fieldName, filter, Document::class.java)).doReturn(mock()) + whenever(wrapped.distinct(fieldName, defaultFilter, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.distinct(fieldName, filter, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.distinct(clientSession.wrapped, fieldName, defaultFilter, BsonDocument::class.java)) + .doReturn(mock()) + whenever(wrapped.distinct(clientSession.wrapped, fieldName, filter, BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.distinct("fieldName", resultClass = Document::class.java) + mongoCollection.distinct("fieldName", filter, Document::class.java) + mongoCollection.distinct(clientSession, "fieldName", resultClass = Document::class.java) + mongoCollection.distinct(clientSession, "fieldName", filter, Document::class.java) + + mongoCollection.distinct("fieldName") + mongoCollection.distinct("fieldName", filter) + mongoCollection.distinct(clientSession, "fieldName") + mongoCollection.distinct(clientSession, "fieldName", filter) + + verify(wrapped).distinct(fieldName, defaultFilter, Document::class.java) + verify(wrapped).distinct(fieldName, filter, Document::class.java) + verify(wrapped).distinct(clientSession.wrapped, fieldName, defaultFilter, Document::class.java) + verify(wrapped).distinct(clientSession.wrapped, fieldName, filter, Document::class.java) + + verify(wrapped).distinct(fieldName, defaultFilter, BsonDocument::class.java) + verify(wrapped).distinct(fieldName, filter, BsonDocument::class.java) + verify(wrapped).distinct(clientSession.wrapped, fieldName, defaultFilter, BsonDocument::class.java) + verify(wrapped).distinct(clientSession.wrapped, fieldName, filter, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingFind() { + val mongoCollection = MongoCollection(wrapped) + + whenever(wrapped.documentClass).doReturn(Document::class.java) + whenever(wrapped.find(defaultFilter, Document::class.java)).doReturn(mock()) + whenever(wrapped.find(filter, Document::class.java)).doReturn(mock()) + whenever(wrapped.find(clientSession.wrapped, defaultFilter, Document::class.java)).doReturn(mock()) + whenever(wrapped.find(clientSession.wrapped, filter, Document::class.java)).doReturn(mock()) + whenever(wrapped.find(defaultFilter, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.find(filter, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.find(clientSession.wrapped, defaultFilter, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.find(clientSession.wrapped, filter, BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.find() + mongoCollection.find(filter) + mongoCollection.find(clientSession) + mongoCollection.find(clientSession, filter) + + mongoCollection.find(resultClass = Document::class.java) + mongoCollection.find(filter, resultClass = Document::class.java) + mongoCollection.find(clientSession, resultClass = Document::class.java) + mongoCollection.find(clientSession, filter, Document::class.java) + + mongoCollection.find() + mongoCollection.find(filter) + mongoCollection.find(clientSession) + mongoCollection.find(clientSession, filter) + + verify(wrapped, times(4)).documentClass + verify(wrapped, times(2)).find(defaultFilter, Document::class.java) + verify(wrapped, times(2)).find(filter, Document::class.java) + verify(wrapped, times(2)).find(clientSession.wrapped, defaultFilter, Document::class.java) + verify(wrapped, times(2)).find(clientSession.wrapped, filter, Document::class.java) + verify(wrapped, times(1)).find(defaultFilter, BsonDocument::class.java) + verify(wrapped, times(1)).find(filter, BsonDocument::class.java) + verify(wrapped, times(1)).find(clientSession.wrapped, defaultFilter, BsonDocument::class.java) + verify(wrapped, times(1)).find(clientSession.wrapped, filter, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingAggregate() { + val mongoCollection = MongoCollection(wrapped) + + whenever(wrapped.documentClass).doReturn(Document::class.java) + whenever(wrapped.aggregate(pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(clientSession.wrapped, pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(pipeline, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(clientSession.wrapped, pipeline, BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.aggregate(pipeline) + mongoCollection.aggregate(clientSession, pipeline) + + mongoCollection.aggregate(pipeline, resultClass = Document::class.java) + mongoCollection.aggregate(clientSession, pipeline, Document::class.java) + + mongoCollection.aggregate(pipeline) + mongoCollection.aggregate(clientSession, pipeline) + + verify(wrapped, times(2)).documentClass + verify(wrapped, times(2)).aggregate(pipeline, Document::class.java) + verify(wrapped, times(2)).aggregate(clientSession.wrapped, pipeline, Document::class.java) + verify(wrapped, times(1)).aggregate(pipeline, BsonDocument::class.java) + verify(wrapped, times(1)).aggregate(clientSession.wrapped, pipeline, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWatch() { + val mongoCollection = MongoCollection(wrapped) + val pipeline = listOf(Document(mapOf("a" to 1))) + + whenever(wrapped.documentClass).doReturn(Document::class.java) + whenever(wrapped.watch(emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(emptyList(), BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.watch() + mongoCollection.watch(pipeline) + mongoCollection.watch(clientSession) + mongoCollection.watch(clientSession, pipeline) + + mongoCollection.watch(resultClass = Document::class.java) + mongoCollection.watch(pipeline, Document::class.java) + mongoCollection.watch(clientSession, resultClass = Document::class.java) + mongoCollection.watch(clientSession, pipeline, Document::class.java) + + mongoCollection.watch() + mongoCollection.watch(pipeline) + mongoCollection.watch(clientSession) + mongoCollection.watch(clientSession, pipeline) + + verify(wrapped, times(4)).documentClass + verify(wrapped, times(2)).watch(emptyList(), Document::class.java) + verify(wrapped, times(2)).watch(pipeline, Document::class.java) + verify(wrapped, times(2)).watch(clientSession.wrapped, emptyList(), Document::class.java) + verify(wrapped, times(2)).watch(clientSession.wrapped, pipeline, Document::class.java) + verify(wrapped, times(1)).watch(emptyList(), BsonDocument::class.java) + verify(wrapped, times(1)).watch(pipeline, BsonDocument::class.java) + verify(wrapped, times(1)).watch(clientSession.wrapped, emptyList(), BsonDocument::class.java) + verify(wrapped, times(1)).watch(clientSession.wrapped, pipeline, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingInsertOne() { + val mongoCollection = MongoCollection(wrapped) + val value = Document("u", 1) + val defaultOptions = InsertOneOptions() + val options = InsertOneOptions().comment("comment") + + whenever(wrapped.insertOne(eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.insertOne(eq(value), eq(options))).doReturn(mock()) + whenever(wrapped.insertOne(eq(clientSession.wrapped), eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.insertOne(eq(clientSession.wrapped), eq(value), eq(options))).doReturn(mock()) + + mongoCollection.insertOne(value) + mongoCollection.insertOne(value, options) + mongoCollection.insertOne(clientSession, value) + mongoCollection.insertOne(clientSession, value, options) + + verify(wrapped).insertOne(eq(value), refEq(defaultOptions)) + verify(wrapped).insertOne(eq(value), eq(options)) + verify(wrapped).insertOne(eq(clientSession.wrapped), eq(value), refEq(defaultOptions)) + verify(wrapped).insertOne(eq(clientSession.wrapped), eq(value), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingInsertMany() { + val mongoCollection = MongoCollection(wrapped) + val value = listOf(Document("u", 1)) + val defaultOptions = InsertManyOptions() + val options = InsertManyOptions().comment("comment") + + whenever(wrapped.insertMany(eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.insertMany(eq(value), eq(options))).doReturn(mock()) + whenever(wrapped.insertMany(eq(clientSession.wrapped), eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.insertMany(eq(clientSession.wrapped), eq(value), eq(options))).doReturn(mock()) + + mongoCollection.insertMany(value) + mongoCollection.insertMany(value, options) + mongoCollection.insertMany(clientSession, value) + mongoCollection.insertMany(clientSession, value, options) + + verify(wrapped).insertMany(eq(value), refEq(defaultOptions)) + verify(wrapped).insertMany(eq(value), eq(options)) + verify(wrapped).insertMany(eq(clientSession.wrapped), eq(value), refEq(defaultOptions)) + verify(wrapped).insertMany(eq(clientSession.wrapped), eq(value), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingBulkWrite() { + val mongoCollection = MongoCollection(wrapped) + val value = listOf(InsertOneModel(Document("u", 1))) + val defaultOptions = BulkWriteOptions() + val options = BulkWriteOptions().comment("comment") + + whenever(wrapped.bulkWrite(eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.bulkWrite(eq(value), eq(options))).doReturn(mock()) + whenever(wrapped.bulkWrite(eq(clientSession.wrapped), eq(value), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.bulkWrite(eq(clientSession.wrapped), eq(value), eq(options))).doReturn(mock()) + + mongoCollection.bulkWrite(value) + mongoCollection.bulkWrite(value, options) + mongoCollection.bulkWrite(clientSession, value) + mongoCollection.bulkWrite(clientSession, value, options) + + verify(wrapped).bulkWrite(eq(value), refEq(defaultOptions)) + verify(wrapped).bulkWrite(eq(value), eq(options)) + verify(wrapped).bulkWrite(eq(clientSession.wrapped), eq(value), refEq(defaultOptions)) + verify(wrapped).bulkWrite(eq(clientSession.wrapped), eq(value), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingUpdateOne() { + val mongoCollection = MongoCollection(wrapped) + val update = Document("u", 1) + val updates = listOf(update) + val defaultOptions = UpdateOptions() + val options = UpdateOptions().comment("comment") + + whenever(wrapped.updateOne(eq(filter), eq(update), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.updateOne(eq(filter), eq(update), eq(options))).doReturn(mock()) + whenever(wrapped.updateOne(eq(filter), eq(updates), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.updateOne(eq(filter), eq(updates), eq(options))).doReturn(mock()) + whenever(wrapped.updateOne(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.updateOne(eq(clientSession.wrapped), eq(filter), eq(update), eq(options))).doReturn(mock()) + whenever(wrapped.updateOne(eq(clientSession.wrapped), eq(filter), eq(updates), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.updateOne(eq(clientSession.wrapped), eq(filter), eq(updates), eq(options))).doReturn(mock()) + + mongoCollection.updateOne(filter, update) + mongoCollection.updateOne(filter, update, options) + mongoCollection.updateOne(filter, updates) + mongoCollection.updateOne(filter, updates, options) + mongoCollection.updateOne(clientSession, filter, update) + mongoCollection.updateOne(clientSession, filter, update, options) + mongoCollection.updateOne(clientSession, filter, updates) + mongoCollection.updateOne(clientSession, filter, updates, options) + + verify(wrapped).updateOne(eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).updateOne(eq(filter), eq(update), eq(options)) + verify(wrapped).updateOne(eq(filter), eq(updates), refEq(defaultOptions)) + verify(wrapped).updateOne(eq(filter), eq(updates), eq(options)) + verify(wrapped).updateOne(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).updateOne(eq(clientSession.wrapped), eq(filter), eq(update), eq(options)) + verify(wrapped).updateOne(eq(clientSession.wrapped), eq(filter), eq(updates), refEq(defaultOptions)) + verify(wrapped).updateOne(eq(clientSession.wrapped), eq(filter), eq(updates), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingUpdateMany() { + val mongoCollection = MongoCollection(wrapped) + val update = Document("u", 1) + val updates = listOf(update) + val defaultOptions = UpdateOptions() + val options = UpdateOptions().comment("comment") + + whenever(wrapped.updateMany(eq(filter), eq(update), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.updateMany(eq(filter), eq(update), eq(options))).doReturn(mock()) + whenever(wrapped.updateMany(eq(filter), eq(updates), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.updateMany(eq(filter), eq(updates), eq(options))).doReturn(mock()) + whenever(wrapped.updateMany(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.updateMany(eq(clientSession.wrapped), eq(filter), eq(update), eq(options))).doReturn(mock()) + whenever(wrapped.updateMany(eq(clientSession.wrapped), eq(filter), eq(updates), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.updateMany(eq(clientSession.wrapped), eq(filter), eq(updates), eq(options))).doReturn(mock()) + + mongoCollection.updateMany(filter, update) + mongoCollection.updateMany(filter, update, options) + mongoCollection.updateMany(filter, updates) + mongoCollection.updateMany(filter, updates, options) + mongoCollection.updateMany(clientSession, filter, update) + mongoCollection.updateMany(clientSession, filter, update, options) + mongoCollection.updateMany(clientSession, filter, updates) + mongoCollection.updateMany(clientSession, filter, updates, options) + + verify(wrapped).updateMany(eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).updateMany(eq(filter), eq(update), eq(options)) + verify(wrapped).updateMany(eq(filter), eq(updates), refEq(defaultOptions)) + verify(wrapped).updateMany(eq(filter), eq(updates), eq(options)) + verify(wrapped).updateMany(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).updateMany(eq(clientSession.wrapped), eq(filter), eq(update), eq(options)) + verify(wrapped).updateMany(eq(clientSession.wrapped), eq(filter), eq(updates), refEq(defaultOptions)) + verify(wrapped).updateMany(eq(clientSession.wrapped), eq(filter), eq(updates), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingReplaceOne() { + val mongoCollection = MongoCollection(wrapped) + val replacement = Document("u", 1) + val defaultOptions = ReplaceOptions() + val options = ReplaceOptions().comment("comment") + + whenever(wrapped.replaceOne(eq(filter), eq(replacement), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.replaceOne(eq(filter), eq(replacement), eq(options))).doReturn(mock()) + whenever(wrapped.replaceOne(eq(clientSession.wrapped), eq(filter), eq(replacement), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.replaceOne(eq(clientSession.wrapped), eq(filter), eq(replacement), eq(options))) + .doReturn(mock()) + + mongoCollection.replaceOne(filter, replacement) + mongoCollection.replaceOne(filter, replacement, options) + mongoCollection.replaceOne(clientSession, filter, replacement) + mongoCollection.replaceOne(clientSession, filter, replacement, options) + + verify(wrapped).replaceOne(eq(filter), eq(replacement), refEq(defaultOptions)) + verify(wrapped).replaceOne(eq(filter), eq(replacement), eq(options)) + verify(wrapped).replaceOne(eq(clientSession.wrapped), eq(filter), eq(replacement), refEq(defaultOptions)) + verify(wrapped).replaceOne(eq(clientSession.wrapped), eq(filter), eq(replacement), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDeleteOne() { + val mongoCollection = MongoCollection(wrapped) + + val defaultOptions = DeleteOptions() + val options = DeleteOptions().comment("comment") + + whenever(wrapped.deleteOne(eq(filter), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.deleteOne(eq(filter), eq(options))).doReturn(mock()) + whenever(wrapped.deleteOne(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.deleteOne(eq(clientSession.wrapped), eq(filter), eq(options))).doReturn(mock()) + + mongoCollection.deleteOne(filter) + mongoCollection.deleteOne(filter, options) + mongoCollection.deleteOne(clientSession, filter) + mongoCollection.deleteOne(clientSession, filter, options) + + verify(wrapped).deleteOne(eq(filter), refEq(defaultOptions)) + verify(wrapped).deleteOne(eq(filter), eq(options)) + verify(wrapped).deleteOne(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions)) + verify(wrapped).deleteOne(eq(clientSession.wrapped), eq(filter), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDeleteMany() { + val mongoCollection = MongoCollection(wrapped) + + val defaultOptions = DeleteOptions() + val options = DeleteOptions().comment("comment") + + whenever(wrapped.deleteMany(eq(filter), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.deleteMany(eq(filter), eq(options))).doReturn(mock()) + whenever(wrapped.deleteMany(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.deleteMany(eq(clientSession.wrapped), eq(filter), eq(options))).doReturn(mock()) + + mongoCollection.deleteMany(filter) + mongoCollection.deleteMany(filter, options) + mongoCollection.deleteMany(clientSession, filter) + mongoCollection.deleteMany(clientSession, filter, options) + + verify(wrapped).deleteMany(eq(filter), refEq(defaultOptions)) + verify(wrapped).deleteMany(eq(filter), eq(options)) + verify(wrapped).deleteMany(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions)) + verify(wrapped).deleteMany(eq(clientSession.wrapped), eq(filter), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingFindOneAndDelete() { + val mongoCollection = MongoCollection(wrapped) + + val defaultOptions = FindOneAndDeleteOptions() + val options = FindOneAndDeleteOptions().comment("comment") + + whenever(wrapped.findOneAndDelete(eq(filter), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.findOneAndDelete(eq(filter), eq(options))).doReturn(mock()) + whenever(wrapped.findOneAndDelete(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.findOneAndDelete(eq(clientSession.wrapped), eq(filter), eq(options))).doReturn(mock()) + + mongoCollection.findOneAndDelete(filter) + mongoCollection.findOneAndDelete(filter, options) + mongoCollection.findOneAndDelete(clientSession, filter) + mongoCollection.findOneAndDelete(clientSession, filter, options) + + verify(wrapped).findOneAndDelete(eq(filter), refEq(defaultOptions)) + verify(wrapped).findOneAndDelete(eq(filter), eq(options)) + verify(wrapped).findOneAndDelete(eq(clientSession.wrapped), eq(filter), refEq(defaultOptions)) + verify(wrapped).findOneAndDelete(eq(clientSession.wrapped), eq(filter), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingFindOneAndUpdate() { + val mongoCollection = MongoCollection(wrapped) + val update = Document("u", 1) + val updateList = listOf(update) + val defaultOptions = FindOneAndUpdateOptions() + val options = FindOneAndUpdateOptions().comment("comment") + + whenever(wrapped.findOneAndUpdate(eq(filter), eq(update), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(filter), eq(update), eq(options))).doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(filter), eq(updateList), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(filter), eq(updateList), eq(options))).doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(update), eq(options))) + .doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(updateList), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(updateList), eq(options))) + .doReturn(mock()) + + mongoCollection.findOneAndUpdate(filter, update) + mongoCollection.findOneAndUpdate(filter, update, options) + mongoCollection.findOneAndUpdate(filter, updateList) + mongoCollection.findOneAndUpdate(filter, updateList, options) + mongoCollection.findOneAndUpdate(clientSession, filter, update) + mongoCollection.findOneAndUpdate(clientSession, filter, update, options) + mongoCollection.findOneAndUpdate(clientSession, filter, updateList) + mongoCollection.findOneAndUpdate(clientSession, filter, updateList, options) + + verify(wrapped).findOneAndUpdate(eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).findOneAndUpdate(eq(filter), eq(update), eq(options)) + verify(wrapped).findOneAndUpdate(eq(filter), eq(updateList), refEq(defaultOptions)) + verify(wrapped).findOneAndUpdate(eq(filter), eq(updateList), eq(options)) + verify(wrapped).findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(update), refEq(defaultOptions)) + verify(wrapped).findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(update), eq(options)) + verify(wrapped).findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(updateList), refEq(defaultOptions)) + verify(wrapped).findOneAndUpdate(eq(clientSession.wrapped), eq(filter), eq(updateList), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingFindOneAndReplace() { + val mongoCollection = MongoCollection(wrapped) + val replacement = Document("u", 1) + val defaultOptions = FindOneAndReplaceOptions() + val options = FindOneAndReplaceOptions().comment("comment") + + whenever(wrapped.findOneAndReplace(eq(filter), eq(replacement), refEq(defaultOptions))).doReturn(mock()) + whenever(wrapped.findOneAndReplace(eq(filter), eq(replacement), eq(options))).doReturn(mock()) + whenever( + wrapped.findOneAndReplace( + eq(clientSession.wrapped), eq(filter), eq(replacement), refEq(defaultOptions))) + .doReturn(mock()) + whenever(wrapped.findOneAndReplace(eq(clientSession.wrapped), eq(filter), eq(replacement), eq(options))) + .doReturn(mock()) + + mongoCollection.findOneAndReplace(filter, replacement) + mongoCollection.findOneAndReplace(filter, replacement, options) + mongoCollection.findOneAndReplace(clientSession, filter, replacement) + mongoCollection.findOneAndReplace(clientSession, filter, replacement, options) + + verify(wrapped).findOneAndReplace(eq(filter), eq(replacement), refEq(defaultOptions)) + verify(wrapped).findOneAndReplace(eq(filter), eq(replacement), eq(options)) + verify(wrapped).findOneAndReplace(eq(clientSession.wrapped), eq(filter), eq(replacement), refEq(defaultOptions)) + verify(wrapped).findOneAndReplace(eq(clientSession.wrapped), eq(filter), eq(replacement), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDrop() { + val mongoCollection = MongoCollection(wrapped) + val defaultOptions = DropCollectionOptions() + val options = DropCollectionOptions().encryptedFields(Document()) + + mongoCollection.drop() + mongoCollection.drop(options) + mongoCollection.drop(clientSession) + mongoCollection.drop(clientSession, options) + + verify(wrapped).drop(refEq(defaultOptions)) + verify(wrapped).drop(eq(options)) + verify(wrapped).drop(eq(clientSession.wrapped), refEq(defaultOptions)) + verify(wrapped).drop(eq(clientSession.wrapped), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingCreateIndex() { + val mongoCollection = MongoCollection(wrapped) + val key = Document() + val defaultOptions = IndexOptions() + val options = IndexOptions().name("name") + + whenever(wrapped.createIndex(eq(key), refEq(defaultOptions))).doReturn("1") + whenever(wrapped.createIndex(eq(key), eq(options))).doReturn("2") + whenever(wrapped.createIndex(eq(clientSession.wrapped), eq(key), refEq(defaultOptions))).doReturn("3") + whenever(wrapped.createIndex(eq(clientSession.wrapped), eq(key), eq(options))).doReturn("4") + + assertEquals("1", mongoCollection.createIndex(key)) + assertEquals("2", mongoCollection.createIndex(key, options)) + assertEquals("3", mongoCollection.createIndex(clientSession, key)) + assertEquals("4", mongoCollection.createIndex(clientSession, key, options)) + + verify(wrapped).createIndex(eq(key), refEq(defaultOptions)) + verify(wrapped).createIndex(eq(key), eq(options)) + verify(wrapped).createIndex(eq(clientSession.wrapped), eq(key), refEq(defaultOptions)) + verify(wrapped).createIndex(eq(clientSession.wrapped), eq(key), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingCreateIndexes() { + val mongoCollection = MongoCollection(wrapped) + val indexes = listOf(IndexModel(Document())) + val defaultOptions = CreateIndexOptions() + val options = CreateIndexOptions().commitQuorum(CreateIndexCommitQuorum.MAJORITY) + + whenever(wrapped.createIndexes(eq(indexes), refEq(defaultOptions))).doReturn(listOf("1")) + whenever(wrapped.createIndexes(eq(indexes), eq(options))).doReturn(listOf("2")) + whenever(wrapped.createIndexes(eq(clientSession.wrapped), eq(indexes), refEq(defaultOptions))) + .doReturn(listOf("3")) + whenever(wrapped.createIndexes(eq(clientSession.wrapped), eq(indexes), eq(options))).doReturn(listOf("4")) + + assertContentEquals(listOf("1"), mongoCollection.createIndexes(indexes)) + assertContentEquals(listOf("2"), mongoCollection.createIndexes(indexes, options)) + assertContentEquals(listOf("3"), mongoCollection.createIndexes(clientSession, indexes)) + assertContentEquals(listOf("4"), mongoCollection.createIndexes(clientSession, indexes, options)) + + verify(wrapped).createIndexes(eq(indexes), refEq(defaultOptions)) + verify(wrapped).createIndexes(eq(indexes), eq(options)) + verify(wrapped).createIndexes(eq(clientSession.wrapped), eq(indexes), refEq(defaultOptions)) + verify(wrapped).createIndexes(eq(clientSession.wrapped), eq(indexes), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingListIndexes() { + val mongoCollection = MongoCollection(wrapped) + + whenever(wrapped.listIndexes(Document::class.java)).doReturn(mock()) + whenever(wrapped.listIndexes(clientSession.wrapped, Document::class.java)).doReturn(mock()) + whenever(wrapped.listIndexes(BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.listIndexes(clientSession.wrapped, BsonDocument::class.java)).doReturn(mock()) + + mongoCollection.listIndexes() + mongoCollection.listIndexes(clientSession) + + mongoCollection.listIndexes(resultClass = Document::class.java) + mongoCollection.listIndexes(clientSession, Document::class.java) + + mongoCollection.listIndexes() + mongoCollection.listIndexes(clientSession) + + verify(wrapped, times(2)).listIndexes(Document::class.java) + verify(wrapped, times(2)).listIndexes(clientSession.wrapped, Document::class.java) + verify(wrapped, times(1)).listIndexes(BsonDocument::class.java) + verify(wrapped, times(1)).listIndexes(clientSession.wrapped, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDropIndex() { + val mongoCollection = MongoCollection(wrapped) + val indexName = "index" + val keys = Document() + val defaultOptions = DropIndexOptions() + val options = DropIndexOptions().maxTime(1, TimeUnit.MILLISECONDS) + + mongoCollection.dropIndex(indexName) + mongoCollection.dropIndex(indexName, options) + mongoCollection.dropIndex(keys) + mongoCollection.dropIndex(keys, options) + mongoCollection.dropIndex(clientSession, indexName) + mongoCollection.dropIndex(clientSession, indexName, options) + mongoCollection.dropIndex(clientSession, keys) + mongoCollection.dropIndex(clientSession, keys, options) + + verify(wrapped).dropIndex(eq(indexName), refEq(defaultOptions)) + verify(wrapped).dropIndex(eq(indexName), eq(options)) + verify(wrapped).dropIndex(eq(keys), refEq(defaultOptions)) + verify(wrapped).dropIndex(eq(keys), eq(options)) + verify(wrapped).dropIndex(eq(clientSession.wrapped), eq(indexName), refEq(defaultOptions)) + verify(wrapped).dropIndex(eq(clientSession.wrapped), eq(indexName), eq(options)) + verify(wrapped).dropIndex(eq(clientSession.wrapped), eq(keys), refEq(defaultOptions)) + verify(wrapped).dropIndex(eq(clientSession.wrapped), eq(keys), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDropIndexes() { + val mongoCollection = MongoCollection(wrapped) + val defaultOptions = DropIndexOptions() + val options = DropIndexOptions().maxTime(1, TimeUnit.MILLISECONDS) + + mongoCollection.dropIndexes() + mongoCollection.dropIndexes(options) + mongoCollection.dropIndexes(clientSession) + mongoCollection.dropIndexes(clientSession, options) + + verify(wrapped).dropIndexes(refEq(defaultOptions)) + verify(wrapped).dropIndexes(eq(options)) + verify(wrapped).dropIndexes(eq(clientSession.wrapped), refEq(defaultOptions)) + verify(wrapped).dropIndexes(eq(clientSession.wrapped), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingRenameCollection() { + val mongoCollection = MongoCollection(wrapped) + val mongoNamespace = MongoNamespace("db", "coll") + val defaultOptions = RenameCollectionOptions() + val options = RenameCollectionOptions().dropTarget(true) + + mongoCollection.renameCollection(mongoNamespace) + mongoCollection.renameCollection(mongoNamespace, options) + mongoCollection.renameCollection(clientSession, mongoNamespace) + mongoCollection.renameCollection(clientSession, mongoNamespace, options) + + verify(wrapped).renameCollection(eq(mongoNamespace), refEq(defaultOptions)) + verify(wrapped).renameCollection(eq(mongoNamespace), eq(options)) + verify(wrapped).renameCollection(eq(clientSession.wrapped), eq(mongoNamespace), refEq(defaultOptions)) + verify(wrapped).renameCollection(eq(clientSession.wrapped), eq(mongoNamespace), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldProvideExtensionFunctionsForTimeBasedOptions() { + val oneThousand = 1000L + + assertEquals(1, CreateIndexOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, CountOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, DropIndexOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, EstimatedDocumentCountOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, FindOneAndDeleteOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, FindOneAndReplaceOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(1, FindOneAndUpdateOptions().maxTime(oneThousand).getMaxTime(TimeUnit.SECONDS)) + assertEquals(oneThousand, IndexOptions().expireAfter(oneThousand).getExpireAfter(TimeUnit.SECONDS)) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCursorTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCursorTest.kt new file mode 100644 index 00000000000..b7530561d73 --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoCursorTest.kt @@ -0,0 +1,71 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ServerAddress +import com.mongodb.ServerCursor +import com.mongodb.client.MongoCursor as JMongoCursor +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoCursorTest { + @Test + fun shouldHaveTheSameMethods() { + val jMongoCursorFunctions = JMongoCursor::class.declaredFunctions.map { it.name }.toSet() + val kMongoCursorFunctions = + MongoCursorImpl::class.declaredFunctions.map { it.name }.toSet() + + MongoCursorImpl::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { + if (it.name == "available") it.name + else "get${it.name.replaceFirstChar{c -> c.uppercaseChar() }}" + } + + assertEquals(jMongoCursorFunctions, kMongoCursorFunctions) + } + + @Test + fun shouldCallTheUnderlyingMethods() { + val wrapped: JMongoCursor = mock() + val cursor = MongoCursorImpl(wrapped) + + whenever(wrapped.serverCursor).doReturn(ServerCursor(1, ServerAddress())) + whenever(wrapped.serverAddress).doReturn(mock()) + + cursor.serverCursor + cursor.serverAddress + cursor.hasNext() + cursor.tryNext() + cursor.available + + verify(wrapped).serverCursor + verify(wrapped).serverAddress + verify(wrapped).hasNext() + verify(wrapped).tryNext() + verify(wrapped).available() + + verifyNoMoreInteractions(wrapped) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoDatabaseTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoDatabaseTest.kt new file mode 100644 index 00000000000..6a7264545dc --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoDatabaseTest.kt @@ -0,0 +1,375 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.ReadConcern +import com.mongodb.ReadPreference +import com.mongodb.WriteConcern +import com.mongodb.client.MongoDatabase as JMongoDatabase +import com.mongodb.client.model.Collation +import com.mongodb.client.model.CreateCollectionOptions +import com.mongodb.client.model.CreateViewOptions +import com.mongodb.client.model.ValidationAction +import com.mongodb.client.model.ValidationOptions +import com.mongodb.kotlin.client.MockitoHelper.deepRefEq +import java.util.concurrent.TimeUnit +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertEquals +import org.bson.BsonDocument +import org.bson.Document +import org.bson.codecs.configuration.CodecRegistry +import org.junit.jupiter.api.Test +import org.mockito.Mock +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.refEq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoDatabaseTest { + + @Mock val wrapped: JMongoDatabase = mock() + @Mock val clientSession: ClientSession = ClientSession(mock()) + + @Test + fun shouldHaveTheSameMethods() { + val jMongoDatabaseFunctions = JMongoDatabase::class.declaredFunctions.map { it.name }.toSet() + val kMongoDatabaseFunctions = + MongoDatabase::class.declaredFunctions.map { it.name }.toSet() + + MongoDatabase::class + .declaredMemberProperties + .filterNot { it.name == "wrapped" } + .map { "get${it.name.replaceFirstChar { c -> c.uppercaseChar() }}" } + + assertEquals(jMongoDatabaseFunctions, kMongoDatabaseFunctions) + } + + @Test + fun shouldCallTheUnderlyingGetNamespace() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.name).doReturn("name") + + mongoDatabase.name + verify(wrapped).name + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetCodecRegistry() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.codecRegistry).doReturn(mock()) + + mongoDatabase.codecRegistry + verify(wrapped).codecRegistry + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetReadPreference() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.readPreference).doReturn(mock()) + + mongoDatabase.readPreference + verify(wrapped).readPreference + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetReadConcern() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.readConcern).doReturn(ReadConcern.DEFAULT) + + mongoDatabase.readConcern + verify(wrapped).readConcern + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetWriteConcern() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.writeConcern).doReturn(mock()) + + mongoDatabase.writeConcern + verify(wrapped).writeConcern + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithCodecRegistry() { + val mongoDatabase = MongoDatabase(wrapped) + val codecRegistry = mock() + whenever(wrapped.withCodecRegistry(codecRegistry)).doReturn(mock()) + + mongoDatabase.withCodecRegistry(codecRegistry) + verify(wrapped).withCodecRegistry(codecRegistry) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithReadPreference() { + val mongoDatabase = MongoDatabase(wrapped) + val readPreference = ReadPreference.primaryPreferred() + whenever(wrapped.withReadPreference(readPreference)).doReturn(mock()) + + mongoDatabase.withReadPreference(readPreference) + verify(wrapped).withReadPreference(readPreference) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithReadConcern() { + val mongoDatabase = MongoDatabase(wrapped) + val readConcern = ReadConcern.AVAILABLE + whenever(wrapped.withReadConcern(readConcern)).doReturn(mock()) + + mongoDatabase.withReadConcern(readConcern) + verify(wrapped).withReadConcern(readConcern) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWithWriteConcern() { + val mongoDatabase = MongoDatabase(wrapped) + val writeConcern = WriteConcern.MAJORITY + whenever(wrapped.withWriteConcern(writeConcern)).doReturn(mock()) + + mongoDatabase.withWriteConcern(writeConcern) + verify(wrapped).withWriteConcern(writeConcern) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingGetCollection() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.getCollection("collectionName", Document::class.java)).doReturn(mock()) + + mongoDatabase.getCollection("collectionName") + verify(wrapped).getCollection("collectionName", Document::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingRunCommand() { + val mongoDatabase = MongoDatabase(wrapped) + val command = Document(mapOf("a" to 1)) + val primary = ReadPreference.primary() + val primaryPreferred = ReadPreference.primaryPreferred() + + whenever(wrapped.readPreference).doReturn(primary) + whenever(wrapped.runCommand(command, primary, Document::class.java)).doReturn(mock()) + whenever(wrapped.runCommand(clientSession.wrapped, command, primary, Document::class.java)).doReturn(mock()) + whenever(wrapped.runCommand(command, primary, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.runCommand(clientSession.wrapped, command, primary, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.runCommand(command, primaryPreferred, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.runCommand(clientSession.wrapped, command, primaryPreferred, BsonDocument::class.java)) + .doReturn(mock()) + + mongoDatabase.runCommand(command) + mongoDatabase.runCommand(command, primary) + mongoDatabase.runCommand(command, resultClass = Document::class.java) + mongoDatabase.runCommand(command, primary, Document::class.java) + + mongoDatabase.runCommand(clientSession, command) + mongoDatabase.runCommand(clientSession, command, primary) + mongoDatabase.runCommand(clientSession, command, resultClass = Document::class.java) + mongoDatabase.runCommand(clientSession, command, primary, Document::class.java) + + mongoDatabase.runCommand(command) + mongoDatabase.runCommand(command, primaryPreferred) + mongoDatabase.runCommand(clientSession, command) + mongoDatabase.runCommand(clientSession, command, primaryPreferred) + + verify(wrapped, times(6)).readPreference + verify(wrapped, times(4)).runCommand(command, primary, Document::class.java) + verify(wrapped, times(4)).runCommand(clientSession.wrapped, command, primary, Document::class.java) + verify(wrapped, times(1)).runCommand(command, primary, BsonDocument::class.java) + verify(wrapped, times(1)).runCommand(clientSession.wrapped, command, primary, BsonDocument::class.java) + verify(wrapped, times(1)).runCommand(command, primaryPreferred, BsonDocument::class.java) + verify(wrapped, times(1)).runCommand(clientSession.wrapped, command, primaryPreferred, BsonDocument::class.java) + + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingDrop() { + val mongoDatabase = MongoDatabase(wrapped) + + mongoDatabase.drop() + mongoDatabase.drop(clientSession) + + verify(wrapped).drop() + verify(wrapped).drop(clientSession.wrapped) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingListCollectionNames() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.listCollectionNames()).doReturn(mock()) + whenever(wrapped.listCollectionNames(clientSession.wrapped)).doReturn(mock()) + + mongoDatabase.listCollectionNames() + mongoDatabase.listCollectionNames(clientSession) + + verify(wrapped).listCollectionNames() + verify(wrapped).listCollectionNames(clientSession.wrapped) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingListCollections() { + val mongoDatabase = MongoDatabase(wrapped) + whenever(wrapped.listCollections(Document::class.java)).doReturn(mock()) + whenever(wrapped.listCollections(BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.listCollections(clientSession.wrapped, Document::class.java)).doReturn(mock()) + whenever(wrapped.listCollections(clientSession.wrapped, BsonDocument::class.java)).doReturn(mock()) + + mongoDatabase.listCollections() + mongoDatabase.listCollections(clientSession) + + mongoDatabase.listCollections(resultClass = Document::class.java) + mongoDatabase.listCollections(clientSession, Document::class.java) + + mongoDatabase.listCollections() + mongoDatabase.listCollections(clientSession) + + verify(wrapped, times(2)).listCollections(Document::class.java) + verify(wrapped, times(2)).listCollections(clientSession.wrapped, Document::class.java) + verify(wrapped, times(1)).listCollections(BsonDocument::class.java) + verify(wrapped, times(1)).listCollections(clientSession.wrapped, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingCreateCollection() { + val mongoDatabase = MongoDatabase(wrapped) + val name = "coll" + val name2 = "coll2" + val defaultOptions = CreateCollectionOptions() + val options = + CreateCollectionOptions().validationOptions(ValidationOptions().validationAction(ValidationAction.WARN)) + + mongoDatabase.createCollection(name) + mongoDatabase.createCollection(name2, options) + mongoDatabase.createCollection(clientSession, name) + mongoDatabase.createCollection(clientSession, name2, options) + + verify(wrapped).createCollection(eq(name), deepRefEq(defaultOptions)) + verify(wrapped).createCollection(eq(name2), eq(options)) + verify(wrapped).createCollection(eq(clientSession.wrapped), eq(name), deepRefEq(defaultOptions)) + verify(wrapped).createCollection(eq(clientSession.wrapped), eq(name2), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingCreateView() { + val mongoDatabase = MongoDatabase(wrapped) + val viewName = "view" + val viewOn = "coll" + val pipeline = listOf(Document(mapOf("a" to 1))) + val defaultOptions = CreateViewOptions() + val options = CreateViewOptions().collation(Collation.builder().backwards(true).build()) + + mongoDatabase.createView(viewName, viewOn, pipeline) + mongoDatabase.createView(viewName, viewOn, pipeline, options) + mongoDatabase.createView(clientSession, viewName, viewOn, pipeline) + mongoDatabase.createView(clientSession, viewName, viewOn, pipeline, options) + + verify(wrapped).createView(eq(viewName), eq(viewOn), eq(pipeline), refEq(defaultOptions)) + verify(wrapped).createView(eq(viewName), eq(viewOn), eq(pipeline), eq(options)) + verify(wrapped) + .createView(eq(clientSession.wrapped), eq(viewName), eq(viewOn), eq(pipeline), refEq(defaultOptions)) + verify(wrapped).createView(eq(clientSession.wrapped), eq(viewName), eq(viewOn), eq(pipeline), eq(options)) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingAggregate() { + val mongoDatabase = MongoDatabase(wrapped) + val pipeline = listOf(Document(mapOf("a" to 1))) + + whenever(wrapped.aggregate(pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(clientSession.wrapped, pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(pipeline, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.aggregate(clientSession.wrapped, pipeline, BsonDocument::class.java)).doReturn(mock()) + + mongoDatabase.aggregate(pipeline) + mongoDatabase.aggregate(clientSession, pipeline) + + mongoDatabase.aggregate(pipeline, resultClass = Document::class.java) + mongoDatabase.aggregate(clientSession, pipeline, Document::class.java) + + mongoDatabase.aggregate(pipeline) + mongoDatabase.aggregate(clientSession, pipeline) + + verify(wrapped, times(2)).aggregate(pipeline, Document::class.java) + verify(wrapped, times(2)).aggregate(clientSession.wrapped, pipeline, Document::class.java) + verify(wrapped, times(1)).aggregate(pipeline, BsonDocument::class.java) + verify(wrapped, times(1)).aggregate(clientSession.wrapped, pipeline, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldCallTheUnderlyingWatch() { + val mongoDatabase = MongoDatabase(wrapped) + val pipeline = listOf(Document(mapOf("a" to 1))) + + whenever(wrapped.watch(emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, Document::class.java)).doReturn(mock()) + whenever(wrapped.watch(emptyList(), BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(pipeline, BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, emptyList(), BsonDocument::class.java)).doReturn(mock()) + whenever(wrapped.watch(clientSession.wrapped, pipeline, BsonDocument::class.java)).doReturn(mock()) + + mongoDatabase.watch() + mongoDatabase.watch(pipeline) + mongoDatabase.watch(clientSession) + mongoDatabase.watch(clientSession, pipeline) + + mongoDatabase.watch(resultClass = Document::class.java) + mongoDatabase.watch(pipeline, Document::class.java) + mongoDatabase.watch(clientSession, resultClass = Document::class.java) + mongoDatabase.watch(clientSession, pipeline, Document::class.java) + + mongoDatabase.watch() + mongoDatabase.watch(pipeline) + mongoDatabase.watch(clientSession) + mongoDatabase.watch(clientSession, pipeline) + + verify(wrapped, times(2)).watch(emptyList(), Document::class.java) + verify(wrapped, times(2)).watch(pipeline, Document::class.java) + verify(wrapped, times(2)).watch(clientSession.wrapped, emptyList(), Document::class.java) + verify(wrapped, times(2)).watch(clientSession.wrapped, pipeline, Document::class.java) + verify(wrapped, times(1)).watch(emptyList(), BsonDocument::class.java) + verify(wrapped, times(1)).watch(pipeline, BsonDocument::class.java) + verify(wrapped, times(1)).watch(clientSession.wrapped, emptyList(), BsonDocument::class.java) + verify(wrapped, times(1)).watch(clientSession.wrapped, pipeline, BsonDocument::class.java) + verifyNoMoreInteractions(wrapped) + } + + @Test + fun shouldProvideExtensionFunctionsForTimeBasedOptions() { + val oneThousand = 1000L + + assertEquals(oneThousand, CreateCollectionOptions().expireAfter(oneThousand).getExpireAfter(TimeUnit.SECONDS)) + } +} diff --git a/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoIterableTest.kt b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoIterableTest.kt new file mode 100644 index 00000000000..e2b0419e49d --- /dev/null +++ b/driver-kotlin-sync/src/test/kotlin/com/mongodb/kotlin/client/MongoIterableTest.kt @@ -0,0 +1,127 @@ +/* + * 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 com.mongodb.kotlin.client + +import com.mongodb.Function +import com.mongodb.client.MongoCursor as JMongoCursor +import com.mongodb.client.MongoIterable as JMongoIterable +import kotlin.test.assertContentEquals +import org.bson.Document +import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever + +class MongoIterableTest { + + @Suppress("UNCHECKED_CAST") + @Test + fun shouldCallTheUnderlyingMethods() { + val delegate: JMongoIterable = mock() + val cursor: JMongoCursor = mock() + val iterable = MongoIterable(delegate) + + val batchSize = 10 + val documents = listOf(Document("a", 1), Document("b", 2), Document("c", 3)) + val transform: (Document) -> String = { it.toJson() } + val transformClass: Class> = + Function::class.java as Class> + + whenever(cursor.hasNext()).thenReturn(true, true, true, false, true, true, true, false, true, true, true, false) + whenever(cursor.next()) + .thenReturn( + documents[0], + documents[1], + documents[2], + documents[0], + documents[1], + documents[2], + documents[0], + documents[1], + documents[2]) + whenever(delegate.cursor()).doReturn(cursor) + whenever(delegate.first()).doReturn(documents[0]) + + whenever(delegate.map(ArgumentMatchers.any(transformClass))).doReturn(mock()) + + iterable.batchSize(batchSize) + iterable.cursor() + iterable.first() + iterable.firstOrNull() + iterable.forEach { it.toString() } + iterable.toCollection(mutableListOf()) + iterable.use { it.take(2) } + iterable.map(transform) + + verify(delegate, times(1)).batchSize(batchSize) + verify(delegate, times(4)).cursor() + verify(delegate, times(2)).first() + verify(delegate, times(1)).map(ArgumentMatchers.any(transformClass)) + + verifyNoMoreInteractions(delegate) + } + + @Test + fun shouldCloseTheUnderlyingCursorWhenUsingUse() { + val delegate: JMongoIterable = mock() + val cursor: JMongoCursor = mock() + val iterable = MongoIterable(delegate) + + val documents = listOf(Document("a", 1), Document("b", 2), Document("c", 3)) + + whenever(cursor.hasNext()).thenReturn(true, true, true, false) + whenever(cursor.next()).thenReturn(documents[0], documents[1], documents[2]) + whenever(delegate.cursor()).doReturn(cursor) + + assertContentEquals(documents.subList(0, 2), iterable.use { it.take(2) }.toList()) + + verify(delegate, times(1)).cursor() + verify(cursor, times(2)).hasNext() + verify(cursor, times(2)).next() + verify(cursor, times(1)).close() + + verifyNoMoreInteractions(delegate) + verifyNoMoreInteractions(cursor) + } + + @Test + fun shouldCloseTheUnderlyingCursorWhenUsingToList() { + val delegate: JMongoIterable = mock() + val cursor: JMongoCursor = mock() + val iterable = MongoIterable(delegate) + + val documents = listOf(Document("a", 1), Document("b", 2), Document("c", 3)) + + whenever(cursor.hasNext()).thenReturn(true, true, true, false) + whenever(cursor.next()).thenReturn(documents[0], documents[1], documents[2]) + whenever(delegate.cursor()).doReturn(cursor) + + assertContentEquals(documents, iterable.toList()) + + verify(delegate, times(1)).cursor() + verify(cursor, times(4)).hasNext() + verify(cursor, times(3)).next() + verify(cursor, times(1)).close() + + verifyNoMoreInteractions(delegate) + verifyNoMoreInteractions(cursor) + } +} diff --git a/settings.gradle b/settings.gradle index 38dcb900739..1e1e661fd5c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,6 +22,7 @@ include ':driver-core' include ':driver-legacy' include ':driver-sync' include ':driver-reactive-streams' +include ':driver-kotlin-sync' include ':bson-scala' include ':driver-scala' include 'util:spock'