From 7964bb8a46561304cd789e543cd47fb0343e68a4 Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Wed, 23 Feb 2022 16:49:09 +0100 Subject: [PATCH 1/6] added BSON Export so that the BSON module is available for all the files within the MongoClient module --- Sources/MongoClient/Exports.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/MongoClient/Exports.swift b/Sources/MongoClient/Exports.swift index 0213dfb1..6fa6d246 100644 --- a/Sources/MongoClient/Exports.swift +++ b/Sources/MongoClient/Exports.swift @@ -1 +1,2 @@ @_exported import MongoCore +@_exported import BSON From 3f158f095d6c8dff1e9e182295d039e93b70a070 Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Wed, 23 Feb 2022 18:20:55 +0100 Subject: [PATCH 2/6] added the server api version to all client classes as specified by the new MongoDB specification https://github.com/mongodb/specifications/blob/master/source/versioned-api/versioned-api.rst --- Sources/MongoClient/Cluster.swift | 19 +++++++++++++------ Sources/MongoClient/Connection.swift | 16 +++++++++++----- Sources/MongoClient/ConnectionPool.swift | 1 + Sources/MongoClient/ServerApi.swift | 18 ++++++++++++++++++ Sources/MongoKitten/MongoDatabase.swift | 18 ++++++++++-------- 5 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 Sources/MongoClient/ServerApi.swift diff --git a/Sources/MongoClient/Cluster.swift b/Sources/MongoClient/Cluster.swift index 5ee1bf8b..4de3510a 100644 --- a/Sources/MongoClient/Cluster.swift +++ b/Sources/MongoClient/Cluster.swift @@ -13,6 +13,8 @@ public typealias _MongoPlatformEventLoopGroup = EventLoopGroup #endif public final class MongoCluster: MongoConnectionPool, @unchecked Sendable { + public let serverApi: ServerApi? + public private(set) var settings: ConnectionSettings { didSet { self.hosts = Set(settings.hosts) @@ -87,12 +89,14 @@ public final class MongoCluster: MongoConnectionPool, @unchecked Sendable { private init( settings: ConnectionSettings, - logger: Logger + logger: Logger, + api: ServerApi? = nil ) { self.settings = settings self.pool = [] self.hosts = Set(settings.hosts) self.logger = logger + self.serverApi = api } /// Connects to a cluster lazily, which means you don't know if the connection was successful until you start querying @@ -100,14 +104,15 @@ public final class MongoCluster: MongoConnectionPool, @unchecked Sendable { /// This is useful when you need a cluster synchronously to query asynchronously public convenience init( lazyConnectingTo settings: ConnectionSettings, - logger: Logger = Logger(label: "org.openkitten.mongokitten.cluster") + logger: Logger = Logger(label: "org.openkitten.mongokitten.cluster"), + api: ServerApi? = nil ) throws { guard settings.hosts.count > 0 else { logger.error("No MongoDB servers were specified while creating a cluster") throw MongoError(.cannotConnect, reason: .noHostSpecified) } - self.init(settings: settings, logger: logger) + self.init(settings: settings, logger: logger, api: api) Task { // Kick off the connection process @@ -120,14 +125,15 @@ public final class MongoCluster: MongoConnectionPool, @unchecked Sendable { public convenience init( connectingTo settings: ConnectionSettings, allowFailure: Bool = false, - logger: Logger = Logger(label: "org.openkitten.mongokitten.cluster") + logger: Logger = Logger(label: "org.openkitten.mongokitten.cluster"), + api: ServerApi? = nil ) async throws { guard settings.hosts.count > 0 else { logger.error("No MongoDB servers were specified while creating a cluster") throw MongoError(.cannotConnect, reason: .noHostSpecified) } - self.init(settings: settings, logger: logger) + self.init(settings: settings, logger: logger, api: api) // Resolve SRV hostnames try await resolveSettings() @@ -234,7 +240,8 @@ public final class MongoCluster: MongoConnectionPool, @unchecked Sendable { logger: logger, onGroup: group, resolver: self.dns, - sessionManager: sessionManager + sessionManager: sessionManager, + api: serverApi ) connection.slaveOk.store(slaveOk) diff --git a/Sources/MongoClient/Connection.swift b/Sources/MongoClient/Connection.swift index 86e36190..fbcec2aa 100644 --- a/Sources/MongoClient/Connection.swift +++ b/Sources/MongoClient/Connection.swift @@ -29,6 +29,8 @@ public struct MongoHandshakeResult { } public final actor MongoConnection: @unchecked Sendable { + public let serverApi: ServerApi? + /// The NIO channel private let channel: Channel public nonisolated var logger: Logger { context.logger } @@ -76,10 +78,11 @@ public final actor MongoConnection: @unchecked Sendable { } /// Creates a connection that can communicate with MongoDB over a channel - public init(channel: Channel, context: MongoClientContext, sessionManager: MongoSessionManager = .init()) { + public init(channel: Channel, context: MongoClientContext, sessionManager: MongoSessionManager = .init(), api: ServerApi? = nil) { self.sessionManager = sessionManager self.channel = channel self.context = context + self.serverApi = api } public static func addHandlers(to channel: Channel, context: MongoClientContext) -> EventLoopFuture { @@ -91,12 +94,13 @@ public final actor MongoConnection: @unchecked Sendable { settings: ConnectionSettings, logger: Logger = Logger(label: "org.openkitten.mongokitten.connection"), resolver: Resolver? = nil, - clientDetails: MongoClientDetails? = nil + clientDetails: MongoClientDetails? = nil, + api: ServerApi? = nil ) async throws -> MongoConnection { #if canImport(NIOTransportServices) && os(iOS) return try await connect(settings: settings, logger: logger, onGroup: NIOTSEventLoopGroup(loopCount: 1), resolver: resolver, clientDetails: clientDetails) #else - return try await connect(settings: settings, logger: logger, onGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1), resolver: resolver, clientDetails: clientDetails) + return try await connect(settings: settings, logger: logger, onGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1), resolver: resolver, clientDetails: clientDetails, api: api) #endif } @@ -106,7 +110,8 @@ public final actor MongoConnection: @unchecked Sendable { onGroup group: _MongoPlatformEventLoopGroup, resolver: Resolver? = nil, clientDetails: MongoClientDetails? = nil, - sessionManager: MongoSessionManager = .init() + sessionManager: MongoSessionManager = .init(), + api: ServerApi? = nil ) async throws -> MongoConnection { let context = MongoClientContext(logger: logger) @@ -154,7 +159,8 @@ public final actor MongoConnection: @unchecked Sendable { let connection = MongoConnection( channel: channel, context: context, - sessionManager: sessionManager + sessionManager: sessionManager, + api: api ) try await connection.authenticate( diff --git a/Sources/MongoClient/ConnectionPool.swift b/Sources/MongoClient/ConnectionPool.swift index 32e203de..e39438a2 100644 --- a/Sources/MongoClient/ConnectionPool.swift +++ b/Sources/MongoClient/ConnectionPool.swift @@ -29,6 +29,7 @@ public protocol MongoConnectionPool { var wireVersion: WireVersion? { get async } var sessionManager: MongoSessionManager { get } var logger: Logger { get } + var serverApi: ServerApi? { get } } extension MongoConnection: MongoConnectionPool { diff --git a/Sources/MongoClient/ServerApi.swift b/Sources/MongoClient/ServerApi.swift new file mode 100644 index 00000000..eb942535 --- /dev/null +++ b/Sources/MongoClient/ServerApi.swift @@ -0,0 +1,18 @@ +import Foundation + +public struct ServerApiVersion { + internal enum _Version: String { + case v1 = "1" + } + + internal let _version: _Version + + public static let V1 = ServerApiVersion(_version: .v1) +} + + +public struct ServerApi { + var version: ServerApiVersion + var strict: Bool? = false + var deprecationErrors: Bool? = false +} diff --git a/Sources/MongoKitten/MongoDatabase.swift b/Sources/MongoKitten/MongoDatabase.swift index 7237ec67..741a049f 100644 --- a/Sources/MongoKitten/MongoDatabase.swift +++ b/Sources/MongoKitten/MongoDatabase.swift @@ -39,15 +39,15 @@ public class MongoDatabase { /// Connect to the database at the given `uri` /// /// - parameter uri: A MongoDB URI that contains at least a database component - public static func lazyConnect(to uri: String) throws -> MongoDatabase { - try lazyConnect(to: ConnectionSettings(uri)) + public static func lazyConnect(to uri: String, api: ServerApi? = nil) throws -> MongoDatabase { + try lazyConnect(to: ConnectionSettings(uri), api: api) } /// Connect to the database at the given `uri` /// /// - parameter uri: A MongoDB URI that contains at least a database component - public static func connect(to uri: String) async throws -> MongoDatabase { - try await connect(to: ConnectionSettings(uri)) + public static func connect(to uri: String, api: ServerApi? = nil) async throws -> MongoDatabase { + try await connect(to: ConnectionSettings(uri), api: api) } /// Connect to the database with the given settings _lazily_. You can also use `lazyConnect(_:on:)` to connect by using a connection string. @@ -56,7 +56,8 @@ public class MongoDatabase { /// /// - parameter settings: The connection settings, which must include a database name public static func lazyConnect( - to settings: ConnectionSettings + to settings: ConnectionSettings, + api: ServerApi? = nil ) throws -> MongoDatabase { let logger = Logger(label: "org.openkitten.mongokitten") guard let targetDatabase = settings.targetDatabase else { @@ -64,7 +65,7 @@ public class MongoDatabase { throw MongoKittenError(.cannotConnect, reason: .noTargetDatabaseSpecified) } - let cluster = try MongoCluster(lazyConnectingTo: settings, logger: logger) + let cluster = try MongoCluster(lazyConnectingTo: settings, logger: logger, api: api) return MongoDatabase(named: targetDatabase, pool: cluster) } @@ -74,7 +75,8 @@ public class MongoDatabase { /// /// - parameter settings: The connection settings, which must include a database name public static func connect( - to settings: ConnectionSettings + to settings: ConnectionSettings, + api: ServerApi? = nil ) async throws -> MongoDatabase { let logger = Logger(label: "org.openkitten.mongokitten") guard let targetDatabase = settings.targetDatabase else { @@ -82,7 +84,7 @@ public class MongoDatabase { throw MongoKittenError(.cannotConnect, reason: .noTargetDatabaseSpecified) } - let cluster = try await MongoCluster(connectingTo: settings, logger: logger) + let cluster = try await MongoCluster(connectingTo: settings, logger: logger, api: api) return MongoDatabase(named: targetDatabase, pool: cluster) } From 39bb2f57c4d0706e8172689fd104bc20f819c927 Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Mon, 28 Feb 2022 19:33:13 +0100 Subject: [PATCH 3/6] api version is now included in every command that is sent to the server, if it was specified. --- Sources/MongoClient/Connection+Execute.swift | 4 ++++ Sources/MongoClient/MongoSingleConnectionPool.swift | 2 +- Sources/MongoClient/ServerApi.swift | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/MongoClient/Connection+Execute.swift b/Sources/MongoClient/Connection+Execute.swift index 69c23f46..23cbbfe5 100644 --- a/Sources/MongoClient/Connection+Execute.swift +++ b/Sources/MongoClient/Connection+Execute.swift @@ -95,6 +95,8 @@ extension MongoConnection { ] as Document, forKey: "lsid") } + if let serverVersion = serverApi { serverVersion.propagateCommand(&command)} + return try await executeMessage( OpQuery( query: command, @@ -119,6 +121,8 @@ extension MongoConnection { ] as Document, forKey: "lsid") } + if let serverVersion = serverApi { serverVersion.propagateCommand(&command)} + // TODO: When retrying a write, don't resend transaction messages except commit & abort if let transaction = transaction { command.appendValue(transaction.number, forKey: "txnNumber") diff --git a/Sources/MongoClient/MongoSingleConnectionPool.swift b/Sources/MongoClient/MongoSingleConnectionPool.swift index bc626eeb..9623972b 100644 --- a/Sources/MongoClient/MongoSingleConnectionPool.swift +++ b/Sources/MongoClient/MongoSingleConnectionPool.swift @@ -2,7 +2,7 @@ import NIO import Logging import MongoCore -public final actor MongoSingleConnectionPool: MongoConnectionPool { +public final actor MongoSingleConnectionPool: MongoConnectionPool { public typealias BuildConnection = @Sendable () async throws -> MongoConnection public var wireVersion: WireVersion? { diff --git a/Sources/MongoClient/ServerApi.swift b/Sources/MongoClient/ServerApi.swift index eb942535..11f51f3a 100644 --- a/Sources/MongoClient/ServerApi.swift +++ b/Sources/MongoClient/ServerApi.swift @@ -1,4 +1,5 @@ import Foundation +import MongoCore public struct ServerApiVersion { internal enum _Version: String { @@ -15,4 +16,10 @@ public struct ServerApi { var version: ServerApiVersion var strict: Bool? = false var deprecationErrors: Bool? = false + + func propagateCommand( _ command: inout Document) { + command.appendValue(self.version._version.rawValue, forKey: "apiVersion") + if let strict = self.strict { command.appendValue(strict, forKey: "apiStrict") } + if let deprecationErrors = self.deprecationErrors { command.appendValue(deprecationErrors, forKey: "apiDeprecationErrors") } + } } From 1aa5c242a5bc5c84e748fe5171b290f38932bc9f Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Mon, 28 Feb 2022 20:02:08 +0100 Subject: [PATCH 4/6] fixed file naming error --- Sources/MongoCore/{Tansaction.swift => Transaction.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/MongoCore/{Tansaction.swift => Transaction.swift} (100%) diff --git a/Sources/MongoCore/Tansaction.swift b/Sources/MongoCore/Transaction.swift similarity index 100% rename from Sources/MongoCore/Tansaction.swift rename to Sources/MongoCore/Transaction.swift From 9d75ece8ecd9681a2a00d89e81ce7c114359460b Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Mon, 28 Feb 2022 20:02:37 +0100 Subject: [PATCH 5/6] added conformance to MongoConnectionPool protocol for MongoSingleConnnectionPool actor --- Sources/MongoClient/MongoSingleConnectionPool.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/MongoClient/MongoSingleConnectionPool.swift b/Sources/MongoClient/MongoSingleConnectionPool.swift index 9623972b..130ed51f 100644 --- a/Sources/MongoClient/MongoSingleConnectionPool.swift +++ b/Sources/MongoClient/MongoSingleConnectionPool.swift @@ -2,7 +2,9 @@ import NIO import Logging import MongoCore -public final actor MongoSingleConnectionPool: MongoConnectionPool { +public final actor MongoSingleConnectionPool: MongoConnectionPool { + public let serverApi: ServerApi? + public typealias BuildConnection = @Sendable () async throws -> MongoConnection public var wireVersion: WireVersion? { @@ -21,11 +23,13 @@ public final actor MongoSingleConnectionPool: MongoConnectionPool { public init( authenticationSource: String = "admin", credentials: ConnectionSettings.Authentication = .unauthenticated, - buildConnection: @escaping BuildConnection + buildConnection: @escaping BuildConnection, + api: ServerApi? = nil ) { self.authenticationSource = authenticationSource self.credentials = credentials self.buildConnection = buildConnection + self.serverApi = api } public func next(for request: ConnectionPoolRequest) async throws -> MongoConnection { From 628a0a353238de46552e86a5ad57fa2930d26285 Mon Sep 17 00:00:00 2001 From: Linus Bohle Date: Wed, 2 Mar 2022 17:48:40 +0100 Subject: [PATCH 6/6] fixed imports for NIOSSL in Package.swift --- Package.swift | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Package.swift b/Package.swift index 6452999f..4bd6869a 100644 --- a/Package.swift +++ b/Package.swift @@ -27,23 +27,23 @@ let package = Package( dependencies: [ // ✏️ .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), - + // 📈 - .package(url: "https://github.com/apple/swift-metrics.git", "1.0.0" ..< "3.0.0"), + .package(url: "https://github.com/apple/swift-metrics.git", "1.0.0" ..< "3.0.0"), // 💾 .package(url: "https://github.com/orlandos-nl/BSON.git", .branch("master/8.0")), -// .package(name: "BSON", path: "../BSON"), + // .package(name: "BSON", path: "../BSON"), // 🚀 - .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), - + .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), + // 📚 .package(url: "https://github.com/orlandos-nl/NioDNS.git", .branch("3.0")), -// .package(name: "DNSClient", path: "../NioDNS"), + // .package(name: "DNSClient", path: "../NioDNS"), // 🔑 - .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0") + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0") ], targets: [ .target( @@ -77,13 +77,19 @@ let package = Package( ), .testTarget( name: "MongoCoreTests", - dependencies: ["MongoCore"]), + dependencies: [ + "MongoCore", + .product(name: "NIOSSL", package: "swift-nio-ssl"), + ]), .testTarget( name: "MongoKittenTests", - dependencies: ["MongoKitten"]), + dependencies: [ + "MongoKitten", + .product(name: "NIOSSL", package: "swift-nio-ssl"), + ]), // TODO: Reimplement these tests -// .testTarget( -// name: "MeowTests", -// dependencies: ["Meow"]), + // .testTarget( + // name: "MeowTests", + // dependencies: ["Meow"]), ] )