diff --git a/Package.swift b/Package.swift index 82f0946..da2c428 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.5 import PackageDescription let package = Package( @@ -10,7 +10,7 @@ let package = Package( .library(name: "Redis", targets: ["Redis"]) ], dependencies: [ - .package(url: "https://gitlab.com/mordil/RediStack.git", from: "1.1.0"), + .package(url: "https://gitlab.com/mordil/RediStack.git", .revisionItem("0465b34ef3f45c45d751100ef572c1afb4b1b50c")), .package(url: "https://github.com/vapor/vapor.git", from: "4.50.0"), ], targets: [ diff --git a/Sources/Redis/Application+Redis.swift b/Sources/Redis/Application+Redis.swift index 62ece9e..49c69eb 100644 --- a/Sources/Redis/Application+Redis.swift +++ b/Sources/Redis/Application+Redis.swift @@ -21,9 +21,8 @@ extension Application { // MARK: RedisClient extension Application.Redis: RedisClient { - public var eventLoop: EventLoop { - self.application.eventLoopGroup.next() - } + public var eventLoop: EventLoop { self.application.eventLoopGroup.next() } + public var defaultLogger: Logger? { self.application.logger } public func logging(to logger: Logger) -> RedisClient { self.application.redis(self.id) @@ -31,49 +30,57 @@ extension Application.Redis: RedisClient { .logging(to: logger) } - public func send(command: String, with arguments: [RESPValue]) -> EventLoopFuture { + public func send( + _ command: RedisCommand, + eventLoop: EventLoop? = nil, + logger: Logger? = nil + ) -> EventLoopFuture { self.application.redis(self.id) .pool(for: self.eventLoop) .logging(to: self.application.logger) - .send(command: command, with: arguments) + .send(command, eventLoop: eventLoop, logger: logger) } public func subscribe( to channels: [RedisChannelName], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) -> EventLoopFuture { self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .subscribe(to: channels, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .subscribe(to: channels, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) } - public func unsubscribe(from channels: [RedisChannelName]) -> EventLoopFuture { + public func unsubscribe(from channels: [RedisChannelName], eventLoop: EventLoop? = nil, logger: Logger? = nil) -> EventLoopFuture { self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .unsubscribe(from: channels) + .unsubscribe(from: channels, eventLoop: eventLoop, logger: logger) } public func psubscribe( to patterns: [String], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) -> EventLoopFuture { self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .psubscribe(to: patterns, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .psubscribe(to: patterns, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) } - public func punsubscribe(from patterns: [String]) -> EventLoopFuture { + public func punsubscribe(from patterns: [String], eventLoop: EventLoop? = nil, logger: Logger? = nil) -> EventLoopFuture { self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .punsubscribe(from: patterns) + .punsubscribe(from: patterns, eventLoop: eventLoop, logger: logger) } } diff --git a/Sources/Redis/Redis+Cache.swift b/Sources/Redis/Redis+Cache.swift index b53f44a..c739362 100644 --- a/Sources/Redis/Redis+Cache.swift +++ b/Sources/Redis/Redis+Cache.swift @@ -82,7 +82,7 @@ private struct RedisCache=5.5) && canImport(_Concurrency) +#if canImport(_Concurrency) import NIOCore import Vapor @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) extension Application.Redis { - public func send(command: String, with arguments: [RESPValue]) async throws -> RESPValue { + public func send( + _ command: RedisCommand, + eventLoop: EventLoop? = nil, + logger: Logger? = nil + ) async throws -> CommandResult { try await self.application.redis(self.id) .pool(for: self.eventLoop) .logging(to: self.application.logger) - .send(command: command, with: arguments).get() + .send(command, eventLoop: eventLoop, logger: logger) + .get() } public func subscribe( to channels: [RedisChannelName], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) async throws { try await self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .subscribe(to: channels, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .subscribe(to: channels, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) .get() } - public func unsubscribe(from channels: [RedisChannelName]) async throws { + public func unsubscribe(from channels: [RedisChannelName], eventLoop: EventLoop?, logger: Logger?) async throws { try await self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .unsubscribe(from: channels) + .unsubscribe(from: channels, eventLoop: eventLoop, logger: logger) .get() } public func psubscribe( to patterns: [String], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) async throws { try await self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .psubscribe(to: patterns, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .psubscribe(to: patterns, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) .get() } - public func punsubscribe(from patterns: [String]) async throws { + public func punsubscribe(from patterns: [String], eventLoop: EventLoop? = nil, logger: Logger? = nil) async throws { try await self.application.redis(self.id) .pubsubClient .logging(to: self.application.logger) - .punsubscribe(from: patterns) + .punsubscribe(from: patterns, eventLoop: eventLoop, logger: logger) .get() } } @@ -75,62 +84,69 @@ extension RedisClient { public func setex(_ key: RedisKey, toJSON entity: E, expirationInSeconds expiration: Int) async throws where E: Encodable { - try await self.setex(key, to: JSONEncoder().encode(entity), expirationInSeconds: expiration).get() + try await self.send(.setex(key, to: JSONEncoder().encode(entity), expirationInSeconds: expiration), eventLoop: nil, logger: nil).get() } } @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) extension Request.Redis { - public func send(command: String, with arguments: [RESPValue]) async throws -> RESPValue { + public func send( + _ command: RedisCommand, + eventLoop: EventLoop? = nil, + logger: Logger? = nil + ) async throws -> CommandResult { try await self.request.application.redis(self.id) .pool(for: self.eventLoop) .logging(to: self.request.logger) - .send(command: command, with: arguments) + .send(command, eventLoop: eventLoop, logger: logger) .get() } public func subscribe( to channels: [RedisChannelName], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) async throws { try await self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .subscribe(to: channels, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .subscribe(to: channels, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) .get() } - public func unsubscribe(from channels: [RedisChannelName]) async throws { + public func unsubscribe(from channels: [RedisChannelName], eventLoop: EventLoop? = nil, logger: Logger? = nil) async throws { try await self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .unsubscribe(from: channels) + .unsubscribe(from: channels, eventLoop: eventLoop, logger: logger) .get() } public func psubscribe( to patterns: [String], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) async throws { try await self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .psubscribe(to: patterns, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .psubscribe(to: patterns, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) .get() } - public func punsubscribe(from patterns: [String]) async throws { + public func punsubscribe(from patterns: [String], eventLoop: EventLoop? = nil, logger: Logger? = nil) async throws { try await self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .punsubscribe(from: patterns) + .punsubscribe(from: patterns, eventLoop: eventLoop, logger: logger) .get() } } - #endif diff --git a/Sources/Redis/RedisClient+Codable.swift b/Sources/Redis/RedisClient+Codable.swift deleted file mode 100644 index 21647d4..0000000 --- a/Sources/Redis/RedisClient+Codable.swift +++ /dev/null @@ -1,35 +0,0 @@ -import AsyncKit -import Foundation - -extension RedisClient { - /// Gets the provided key as a decodable type. - public func get(_ key: RedisKey, asJSON type: D.Type) -> EventLoopFuture - where D: Decodable - { - return self.get(key, as: Data.self).flatMapThrowing { data in - return try data.flatMap { try JSONDecoder().decode(D.self, from: $0) } - } - } - - /// Sets key to an encodable item. - public func set(_ key: RedisKey, toJSON entity: E) -> EventLoopFuture - where E: Encodable - { - do { - return try self.set(key, to: JSONEncoder().encode(entity)) - } catch { - return self.eventLoop.makeFailedFuture(error) - } - } - - /// Sets key to an encodable item with an expiration time. - public func setex(_ key: RedisKey, toJSON entity: E, expirationInSeconds expiration: Int) -> EventLoopFuture - where E: Encodable - { - do { - return try self.setex(key, to: JSONEncoder().encode(entity), expirationInSeconds: expiration) - } catch { - return self.eventLoop.makeFailedFuture(error) - } - } -} diff --git a/Sources/Redis/RedisConfiguration.swift b/Sources/Redis/RedisConfiguration.swift index 7f76697..79573ba 100644 --- a/Sources/Redis/RedisConfiguration.swift +++ b/Sources/Redis/RedisConfiguration.swift @@ -3,43 +3,68 @@ @_exported import struct NIO.TimeAmount import enum NIO.SocketAddress -/// Configuration for connecting to a Redis instance -public struct RedisConfiguration { +/// Configuration for connection to one or more Redis instances with Vapor. +public typealias RedisConfiguration = RedisConnectionPool.Configuration + +extension RedisConfiguration { public typealias ValidationError = RedisConnection.Configuration.ValidationError - public var serverAddresses: [SocketAddress] - public var password: String? - public var database: Int? - public var pool: PoolOptions + public static var defaultConnectionCountBehavior: RedisConnectionPool.ConnectionCountBehavior { + return .strict(maximumConnectionCount: 2, minimumConnectionCount: 0) + } + public static var defaultRetryStrategy: RedisConnectionPool.PoolConnectionRetryStrategy { .exponentialBackoff() } +} - public struct PoolOptions { - public var maximumConnectionCount: RedisConnectionPoolSize - public var minimumConnectionCount: Int - public var connectionBackoffFactor: Float32 - public var initialConnectionBackoffDelay: TimeAmount - public var connectionRetryTimeout: TimeAmount? +// MARK: Convenience Initializers - public init( - maximumConnectionCount: RedisConnectionPoolSize = .maximumActiveConnections(2), - minimumConnectionCount: Int = 0, - connectionBackoffFactor: Float32 = 2, - initialConnectionBackoffDelay: TimeAmount = .milliseconds(100), - connectionRetryTimeout: TimeAmount? = nil - ) { - self.maximumConnectionCount = maximumConnectionCount - self.minimumConnectionCount = minimumConnectionCount - self.connectionBackoffFactor = connectionBackoffFactor - self.initialConnectionBackoffDelay = initialConnectionBackoffDelay - self.connectionRetryTimeout = connectionRetryTimeout - } +extension RedisConfiguration { + public init( + hostname: String, + port: Int = RedisConnection.Configuration.defaultPort, + password: String? = nil, + database: Int? = nil, + connectionCountBehavior: RedisConnectionPool.ConnectionCountBehavior = Self.defaultConnectionCountBehavior, + connectionRetryStrategy: RedisConnectionPool.PoolConnectionRetryStrategy = Self.defaultRetryStrategy, + poolDefaultLogger: Logger? = nil, + connectionDefaultLogger: Logger? = nil + ) throws { + self.init( + initialServerConnectionAddresses: [try .makeAddressResolvingHost(hostname, port: port)], + connectionCountBehavior: connectionCountBehavior, + connectionConfiguration: .init( + initialDatabase: database, + password: password, + defaultLogger: connectionDefaultLogger + ), + retryStrategy: connectionRetryStrategy, + poolDefaultLogger: poolDefaultLogger + ) } - public init(url string: String, pool: PoolOptions = .init()) throws { + public init( + url string: String, + connectionCountBehavior: RedisConnectionPool.ConnectionCountBehavior = Self.defaultConnectionCountBehavior, + connectionRetryStrategy: RedisConnectionPool.PoolConnectionRetryStrategy = Self.defaultRetryStrategy, + poolDefaultLogger: Logger? = nil, + connectionDefaultLogger: Logger? = nil + ) throws { guard let url = URL(string: string) else { throw ValidationError.invalidURLString } - try self.init(url: url, pool: pool) + try self.init( + url: url, + connectionCountBehavior: connectionCountBehavior, + connectionRetryStrategy: connectionRetryStrategy, + poolDefaultLogger: poolDefaultLogger, + connectionDefaultLogger: connectionDefaultLogger + ) } - public init(url: URL, pool: PoolOptions = .init()) throws { + public init( + url: URL, + connectionCountBehavior: RedisConnectionPool.ConnectionCountBehavior = Self.defaultConnectionCountBehavior, + connectionRetryStrategy: RedisConnectionPool.PoolConnectionRetryStrategy = Self.defaultRetryStrategy, + poolDefaultLogger: Logger? = nil, + connectionDefaultLogger: Logger? = nil + ) throws { guard let scheme = url.scheme, !scheme.isEmpty @@ -52,56 +77,57 @@ public struct RedisConfiguration { port: url.port ?? RedisConnection.Configuration.defaultPort, password: url.password, database: Int(url.lastPathComponent), - pool: pool + connectionCountBehavior: connectionCountBehavior, + connectionRetryStrategy: connectionRetryStrategy, + poolDefaultLogger: poolDefaultLogger, + connectionDefaultLogger: connectionDefaultLogger ) } public init( - hostname: String, - port: Int = RedisConnection.Configuration.defaultPort, + serverAddresses: [SocketAddress], password: String? = nil, database: Int? = nil, - pool: PoolOptions = .init() - ) throws { - if database != nil && database! < 0 { throw ValidationError.outOfBoundsDatabaseID } - - try self.init( - serverAddresses: [.makeAddressResolvingHost(hostname, port: port)], - password: password, - database: database, - pool: pool + connectionCountBehavior: RedisConnectionPool.ConnectionCountBehavior = Self.defaultConnectionCountBehavior, + connectionRetryStrategy: RedisConnectionPool.PoolConnectionRetryStrategy = Self.defaultRetryStrategy, + poolDefaultLogger: Logger? = nil, + connectionDefaultLogger: Logger? = nil + ) { + self.init( + initialServerConnectionAddresses: serverAddresses, + connectionCountBehavior: connectionCountBehavior, + connectionConfiguration: .init( + initialDatabase: database, + password: password, + defaultLogger: connectionDefaultLogger + ), + retryStrategy: connectionRetryStrategy, + poolDefaultLogger: poolDefaultLogger ) } +} - public init( - serverAddresses: [SocketAddress], - password: String? = nil, - database: Int? = nil, - pool: PoolOptions = .init() - ) throws { - self.serverAddresses = serverAddresses - self.password = password - self.database = database - self.pool = pool +// MARK: Internal Configuration Creation + +extension RedisConnectionPool.PoolConnectionConfiguration { + internal func logging(to newLogger: Logger) -> Self { + return .init( + initialDatabase: self.initialDatabase, + password: self.password, + defaultLogger: newLogger, + tcpClient: self.tcpClient + ) } } extension RedisConnectionPool.Configuration { - internal init(_ config: RedisConfiguration, defaultLogger: Logger) { - self.init( - initialServerConnectionAddresses: config.serverAddresses, - maximumConnectionCount: config.pool.maximumConnectionCount, - connectionFactoryConfiguration: .init( - connectionInitialDatabase: config.database, - connectionPassword: config.password, - connectionDefaultLogger: defaultLogger, - tcpClient: nil - ), - minimumConnectionCount: config.pool.minimumConnectionCount, - connectionBackoffFactor: config.pool.connectionBackoffFactor, - initialConnectionBackoffDelay: config.pool.initialConnectionBackoffDelay, - connectionRetryTimeout: config.pool.connectionRetryTimeout, - poolDefaultLogger: defaultLogger + internal func logging(to newLogger: Logger) -> Self { + return .init( + initialServerConnectionAddresses: self.initialConnectionAddresses, + connectionCountBehavior: self.connectionCountBehavior, + connectionConfiguration: self.connectionConfiguration.logging(to: newLogger), + retryStrategy: self.retryStrategy, + poolDefaultLogger: newLogger ) } } diff --git a/Sources/Redis/RedisStorage.swift b/Sources/Redis/RedisStorage.swift index 5d1c637..0b1a501 100644 --- a/Sources/Redis/RedisStorage.swift +++ b/Sources/Redis/RedisStorage.swift @@ -1,3 +1,4 @@ +import struct NIOConcurrencyHelpers.NIOLock import Vapor extension Application { @@ -17,7 +18,7 @@ extension Application { } final class RedisStorage { - private var lock: Lock + private var lock: NIOLock private var configurations: [RedisID: RedisConfiguration] fileprivate var pools: [PoolKey: RedisConnectionPool] { willSet { @@ -75,7 +76,7 @@ extension RedisStorage { let newKey: PoolKey = PoolKey(eventLoopKey: eventLoop.key, redisID: redisID) let newPool = RedisConnectionPool( - configuration: .init(configuration, defaultLogger: application.logger), + configuration: configuration.logging(to: application.logger), boundEventLoop: eventLoop) newPools[newKey] = newPool diff --git a/Sources/Redis/Request+Redis.swift b/Sources/Redis/Request+Redis.swift index e4cbad4..2624f54 100644 --- a/Sources/Redis/Request+Redis.swift +++ b/Sources/Redis/Request+Redis.swift @@ -16,9 +16,8 @@ extension Request { // MARK: RedisClient extension Request.Redis: RedisClient { - public var eventLoop: EventLoop { - self.request.eventLoop - } + public var eventLoop: EventLoop { self.request.eventLoop } + public var defaultLogger: Logger? { self.request.logger } public func logging(to logger: Logger) -> RedisClient { self.request.application.redis(self.id) @@ -26,49 +25,57 @@ extension Request.Redis: RedisClient { .logging(to: logger) } - public func send(command: String, with arguments: [RESPValue]) -> EventLoopFuture { + public func send( + _ command: RedisCommand, + eventLoop: EventLoop? = nil, + logger: Logger? = nil + ) -> EventLoopFuture { self.request.application.redis(self.id) .pool(for: self.eventLoop) .logging(to: self.request.logger) - .send(command: command, with: arguments) + .send(command, eventLoop: eventLoop, logger: logger) } public func subscribe( to channels: [RedisChannelName], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) -> EventLoopFuture { self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .subscribe(to: channels, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .subscribe(to: channels, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) } - public func unsubscribe(from channels: [RedisChannelName]) -> EventLoopFuture { + public func unsubscribe(from channels: [RedisChannelName], eventLoop: EventLoop? = nil, logger: Logger? = nil) -> EventLoopFuture { self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .unsubscribe(from: channels) + .unsubscribe(from: channels, eventLoop: eventLoop, logger: logger) } public func psubscribe( to patterns: [String], + eventLoop: EventLoop? = nil, + logger: Logger? = nil, messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver, - onSubscribe subscribeHandler: RedisSubscriptionChangeHandler?, - onUnsubscribe unsubscribeHandler: RedisSubscriptionChangeHandler? + onSubscribe subscribeHandler: RedisSubscribeHandler?, + onUnsubscribe unsubscribeHandler: RedisUnsubscribeHandler? ) -> EventLoopFuture { self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .psubscribe(to: patterns, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) + .psubscribe(to: patterns, eventLoop: eventLoop, logger: logger, messageReceiver: receiver, onSubscribe: subscribeHandler, onUnsubscribe: unsubscribeHandler) } - public func punsubscribe(from patterns: [String]) -> EventLoopFuture { + public func punsubscribe(from patterns: [String], eventLoop: EventLoop? = nil, logger: Logger? = nil) -> EventLoopFuture { self.request.application.redis(self.id) .pubsubClient .logging(to: self.request.logger) - .punsubscribe(from: patterns) + .punsubscribe(from: patterns, eventLoop: eventLoop, logger: logger) } } diff --git a/Tests/RedisTests/MultipleRedisTests.swift b/Tests/RedisTests/MultipleRedisTests.swift index 3824ef5..7835fdf 100644 --- a/Tests/RedisTests/MultipleRedisTests.swift +++ b/Tests/RedisTests/MultipleRedisTests.swift @@ -34,11 +34,13 @@ class MultipleRedisTests: XCTestCase { try app.boot() - let info1 = try app.redis(.one).send(command: "INFO").wait() - XCTAssertContains(info1.string, "redis_version") + let infoCommand = RedisCommand(keyword: "INFO", arguments: []) - let info2 = try app.redis(.two).send(command: "INFO").wait() - XCTAssertContains(info2.string, "redis_version") + let info1 = try app.redis(.one).send(infoCommand).wait() + XCTAssertContains(info1, "redis_version") + + let info2 = try app.redis(.two).send(infoCommand).wait() + XCTAssertContains(info2, "redis_version") } func testSetAndGet() throws { @@ -49,14 +51,10 @@ class MultipleRedisTests: XCTestCase { app.redis(.two).configuration = redisConfig2 app.get("test1") { req in - req.redis(.one).get("name").map { - $0.description - } + req.redis(.one).get("name").map { $0 ?? "nil" } } app.get("test2") { req in - req.redis(.two).get("name").map { - $0.description - } + req.redis(.two).get("name").map { $0 ?? "nil" } } try app.boot() @@ -72,7 +70,7 @@ class MultipleRedisTests: XCTestCase { XCTAssertEqual(res.body.string, "redis2") } - XCTAssertEqual("redis1", try app.redis(.one).get("name").wait().string) - XCTAssertEqual("redis2", try app.redis(.two).get("name").wait().string) + XCTAssertEqual("redis1", try app.redis(.one).get("name").wait()) + XCTAssertEqual("redis2", try app.redis(.two).get("name").wait()) } } diff --git a/Tests/RedisTests/RedisTests.swift b/Tests/RedisTests/RedisTests.swift index ff89bfd..31d7ce1 100644 --- a/Tests/RedisTests/RedisTests.swift +++ b/Tests/RedisTests/RedisTests.swift @@ -24,6 +24,11 @@ final class RedisTests: XCTestCase { } // MARK: Core RediStack integration + +extension RedisCommand { + fileprivate static var info: RedisCommand { .init(keyword: "INFO", arguments: []) } +} + extension RedisTests { func testApplicationRedis() throws { let app = Application() @@ -32,8 +37,9 @@ extension RedisTests { app.redis.configuration = redisConfig try app.boot() - let info = try app.redis.send(command: "INFO").wait() - XCTAssertContains(info.string, "redis_version") + + let info = try app.redis.send(.info).wait() + XCTAssertContains(info, "redis_version") } func testRouteHandlerRedis() throws { @@ -43,9 +49,7 @@ extension RedisTests { app.redis.configuration = redisConfig app.get("test") { req in - req.redis.send(command: "INFO").map { - $0.description - } + req.redis.send(.info) } try app.test(.GET, "test") { res in @@ -62,8 +66,8 @@ extension RedisTests { let redisConfiguration = try RedisConfiguration(url: urlStr!) - XCTAssertEqual(redisConfiguration.password, "password") - XCTAssertEqual(redisConfiguration.database, 0) + XCTAssertEqual(redisConfiguration.connectionConfiguration.password, "password") + XCTAssertEqual(redisConfiguration.connectionConfiguration.initialDatabase, 0) } } @@ -102,11 +106,11 @@ extension RedisTests { app.get("test") { $0.redis .withBorrowedClient { client in - return client.send(command: "MULTI") - .flatMap { _ in client.send(command: "PING") } + return client.send(RedisCommand(keyword: "MULTI", arguments: []), eventLoop: nil, logger: nil) + .flatMap { _ in client.send(.ping(), eventLoop: nil, logger: nil) } .flatMap { queuedResponse -> EventLoopFuture in - XCTAssertEqual(queuedResponse.string, "QUEUED") - return client.send(command: "EXEC") + XCTAssertEqual(queuedResponse, "QUEUED") + return client.send(RedisCommand(keyword: "EXEC", arguments: []), eventLoop: nil, logger: nil) } } .map { result -> [String] in @@ -129,12 +133,12 @@ extension RedisTests { let result = try app.redis .withBorrowedConnection { client in - return client.send(command: "MULTI") - .flatMap { _ in client.send(command: "PING") } - .flatMap { queuedResponse -> EventLoopFuture in - XCTAssertEqual(queuedResponse.string, "QUEUED") - return client.send(command: "EXEC") - } + return client.send(RedisCommand(keyword: "MULTI", arguments: []), eventLoop: nil, logger: nil) + .flatMap { _ in client.send(.ping(), eventLoop: nil, logger: nil) } + .flatMap { queuedResponse -> EventLoopFuture in + XCTAssertEqual(queuedResponse, "QUEUED") + return client.send(RedisCommand(keyword: "EXEC", arguments: []), eventLoop: nil, logger: nil) + } } .map { result -> [String] in guard let response = result.array else { return [] }