diff --git a/README.md b/README.md index 0402b12..5c4cabe 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is the Hummingbird interface to [RediStack library](https://gitlab.com/mord ## Usage ```swift -let app = HBApplication() +let app = Application() try app.addRedis(configuration: .init(hostname: "localhost", port: 6379)) app.router.get("redis") { request in request.redis.send(command: "INFO").map { diff --git a/Sources/HummingbirdJobsRedis/Configuration.swift b/Sources/HummingbirdJobsRedis/Configuration.swift index 13c11b4..9fa7ae3 100644 --- a/Sources/HummingbirdJobsRedis/Configuration.swift +++ b/Sources/HummingbirdJobsRedis/Configuration.swift @@ -15,7 +15,7 @@ import NIOCore @preconcurrency import RediStack -extension HBRedisQueue { +extension RedisQueue { /// what to do with failed/processing jobs from last time queue was handled public enum JobInitialization: Sendable { case doNothing diff --git a/Sources/HummingbirdJobsRedis/RedisJobQueue.swift b/Sources/HummingbirdJobsRedis/RedisJobQueue.swift index e909d5e..568de27 100644 --- a/Sources/HummingbirdJobsRedis/RedisJobQueue.swift +++ b/Sources/HummingbirdJobsRedis/RedisJobQueue.swift @@ -23,7 +23,7 @@ import NIOCore import RediStack /// Redis implementation of job queue driver -public final class HBRedisQueue: HBJobQueueDriver { +public final class RedisQueue: JobQueueDriver { public struct JobID: Sendable, CustomStringConvertible { let id: String @@ -67,7 +67,7 @@ public final class HBRedisQueue: HBJobQueueDriver { } } - let redisConnectionPool: HBRedisConnectionPoolService + let redisConnectionPool: RedisConnectionPoolService let configuration: Configuration let isStopped: ManagedAtomic @@ -75,7 +75,7 @@ public final class HBRedisQueue: HBJobQueueDriver { /// - Parameters: /// - redisConnectionPoolService: Redis connection pool /// - configuration: configuration - public init(_ redisConnectionPoolService: HBRedisConnectionPoolService, configuration: Configuration = .init()) { + public init(_ redisConnectionPoolService: RedisConnectionPoolService, configuration: Configuration = .init()) { self.redisConnectionPool = redisConnectionPoolService self.configuration = configuration self.isStopped = .init(false) @@ -132,7 +132,7 @@ public final class HBRedisQueue: HBJobQueueDriver { /// Pop Job off queue and add to pending queue /// - Parameter eventLoop: eventLoop to do work on /// - Returns: queued job - func popFirst() async throws -> HBQueuedJob? { + func popFirst() async throws -> QueuedJob? { let pool = self.redisConnectionPool.pool let key = try await pool.rpoplpush(from: self.configuration.queueKey, to: self.configuration.processingQueueKey).get() guard !key.isNull else { @@ -199,11 +199,11 @@ public final class HBRedisQueue: HBJobQueueDriver { } } -/// extend HBRedisJobQueue to conform to AsyncSequence -extension HBRedisQueue { - public typealias Element = HBQueuedJob +/// extend RedisJobQueue to conform to AsyncSequence +extension RedisQueue { + public typealias Element = QueuedJob public struct AsyncIterator: AsyncIteratorProtocol { - let queue: HBRedisQueue + let queue: RedisQueue public func next() async throws -> Element? { while true { @@ -224,12 +224,12 @@ extension HBRedisQueue { } } -extension HBJobQueueDriver where Self == HBRedisQueue { +extension JobQueueDriver where Self == RedisQueue { /// Return Redis driver for Job Queue /// - Parameters: /// - redisConnectionPoolService: Redis connection pool /// - configuration: configuration - public static func redis(_ redisConnectionPoolService: HBRedisConnectionPoolService, configuration: HBRedisQueue.Configuration = .init()) -> Self { + public static func redis(_ redisConnectionPoolService: RedisConnectionPoolService, configuration: RedisQueue.Configuration = .init()) -> Self { .init(redisConnectionPoolService, configuration: configuration) } } diff --git a/Sources/HummingbirdRedis/Deprecations.swift b/Sources/HummingbirdRedis/Deprecations.swift new file mode 100644 index 0000000..bb017d6 --- /dev/null +++ b/Sources/HummingbirdRedis/Deprecations.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Hummingbird server framework project +// +// Copyright (c) 2024 the Hummingbird authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// Below is a list of deprecated symbols with the "HB" prefix. These are available +// temporarily to ease transition from the old symbols that included the "HB" +// prefix to the new ones. +// +// This file will be removed before we do a 2.0 release + +@_documentation(visibility: internal) @available(*, deprecated, renamed: "RedisConfiguration") +public typealias HBRedisConfiguration = RedisConfiguration +@_documentation(visibility: internal) @available(*, deprecated, renamed: "RedisConnectionPoolService") +public typealias HBRedisConnectionPoolService = RedisConnectionPoolService +@_documentation(visibility: internal) @available(*, deprecated, renamed: "RedisPersistDriver") +public typealias HBRedisPersistDriver = RedisPersistDriver diff --git a/Sources/HummingbirdRedis/Persist+Redis.swift b/Sources/HummingbirdRedis/Persist+Redis.swift index 48d11a2..9643ee5 100644 --- a/Sources/HummingbirdRedis/Persist+Redis.swift +++ b/Sources/HummingbirdRedis/Persist+Redis.swift @@ -16,14 +16,14 @@ import Hummingbird import RediStack /// Redis driver for persist system for storing persistent cross request key/value pairs -public struct HBRedisPersistDriver: HBPersistDriver { - let redisConnectionPool: HBRedisConnectionPoolService +public struct RedisPersistDriver: PersistDriver { + let redisConnectionPool: RedisConnectionPoolService - public init(redisConnectionPoolService: HBRedisConnectionPoolService) { + public init(redisConnectionPoolService: RedisConnectionPoolService) { self.redisConnectionPool = redisConnectionPoolService } - /// create new key with value. If key already exist throw `HBPersistError.duplicate` error + /// create new key with value. If key already exist throw `PersistError.duplicate` error public func create(key: String, value: some Codable, expires: Duration?) async throws { let expiration: RedisSetCommandExpiration? = expires.map { .seconds(Int($0.components.seconds)) } let result = try await self.redisConnectionPool.set(.init(key), toJSON: value, onCondition: .keyDoesNotExist, expiration: expiration).get() @@ -31,7 +31,7 @@ public struct HBRedisPersistDriver: HBPersistDriver { case .ok: return case .conditionNotMet: - throw HBPersistError.duplicate + throw PersistError.duplicate } } diff --git a/Sources/HummingbirdRedis/RedisConfiguration.swift b/Sources/HummingbirdRedis/RedisConfiguration.swift index 39e4d56..1c89fac 100644 --- a/Sources/HummingbirdRedis/RedisConfiguration.swift +++ b/Sources/HummingbirdRedis/RedisConfiguration.swift @@ -20,7 +20,7 @@ import Logging import NIOCore import RediStack -public struct HBRedisConfiguration { +public struct RedisConfiguration { public typealias ValidationError = RedisConnection.Configuration.ValidationError public var serverAddresses: [SocketAddress] @@ -104,7 +104,7 @@ public struct HBRedisConfiguration { extension RedisConnectionPool.Configuration { init( - _ config: HBRedisConfiguration, + _ config: RedisConfiguration, logger: Logger ) { self.init( diff --git a/Sources/HummingbirdRedis/RedisConnectionPoolService.swift b/Sources/HummingbirdRedis/RedisConnectionPoolService.swift index b668803..42105c1 100644 --- a/Sources/HummingbirdRedis/RedisConnectionPoolService.swift +++ b/Sources/HummingbirdRedis/RedisConnectionPoolService.swift @@ -20,10 +20,10 @@ import RediStack import ServiceLifecycle /// Wrapper for RedisConnectionPool that conforms to ServiceLifecycle Service -public struct HBRedisConnectionPoolService: Service, @unchecked Sendable { +public struct RedisConnectionPoolService: Service, @unchecked Sendable { /// Initialize RedisConnectionPoolService public init( - _ config: HBRedisConfiguration, + _ config: RedisConfiguration, eventLoopGroupProvider: EventLoopGroupProvider = .singleton, logger: Logger ) { @@ -66,7 +66,7 @@ public struct HBRedisConnectionPoolService: Service, @unchecked Sendable { } } -extension HBRedisConnectionPoolService { +extension RedisConnectionPoolService { /// A unique identifer to represent this connection. @inlinable public var id: UUID { self.pool.id } @@ -124,7 +124,7 @@ extension HBRedisConnectionPoolService { } } -extension HBRedisConnectionPoolService: RedisClient { +extension RedisConnectionPoolService: RedisClient { @inlinable public var eventLoop: NIOCore.EventLoop { self.pool.eventLoop } diff --git a/Tests/HummingbirdJobsRedisTests/RedisJobsTests.swift b/Tests/HummingbirdJobsRedisTests/RedisJobsTests.swift index 6911baf..d40ae77 100644 --- a/Tests/HummingbirdJobsRedisTests/RedisJobsTests.swift +++ b/Tests/HummingbirdJobsRedisTests/RedisJobsTests.swift @@ -40,7 +40,7 @@ final class HummingbirdRedisJobsTests: XCTestCase { #endif } - static let env = HBEnvironment() + static let env = Environment() static let redisHostname = env.get("REDIS_HOSTNAME") ?? "localhost" /// Helper function for test a server @@ -49,16 +49,16 @@ final class HummingbirdRedisJobsTests: XCTestCase { /// shutdown correctly @discardableResult public func testJobQueue( numWorkers: Int, - failedJobsInitialization: HBRedisQueue.JobInitialization = .remove, - test: (HBJobQueue) async throws -> T + failedJobsInitialization: RedisQueue.JobInitialization = .remove, + test: (JobQueue) async throws -> T ) async throws -> T { var logger = Logger(label: "RedisJobsTests") logger.logLevel = .debug - let redisService = try HBRedisConnectionPoolService( + let redisService = try RedisConnectionPoolService( .init(hostname: Self.redisHostname, port: 6379), logger: logger ) - let jobQueue = HBJobQueue( + let jobQueue = JobQueue( .redis( redisService, configuration: .init( @@ -90,9 +90,9 @@ final class HummingbirdRedisJobsTests: XCTestCase { func testBasic() async throws { let expectation = XCTestExpectation(description: "TestJob.execute was called", expectedFulfillmentCount: 10) - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) try await self.testJobQueue(numWorkers: 1) { jobQueue in - jobQueue.registerJob(jobIdentifer) { parameters, context in + jobQueue.registerJob(id: jobIdentifer) { parameters, context in context.logger.info("Parameters=\(parameters)") try await Task.sleep(for: .milliseconds(Int.random(in: 10..<50))) expectation.fulfill() @@ -113,13 +113,13 @@ final class HummingbirdRedisJobsTests: XCTestCase { } func testMultipleWorkers() async throws { - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) let runningJobCounter = ManagedAtomic(0) let maxRunningJobCounter = ManagedAtomic(0) let expectation = XCTestExpectation(description: "TestJob.execute was called", expectedFulfillmentCount: 10) try await self.testJobQueue(numWorkers: 4) { jobQueue in - jobQueue.registerJob(jobIdentifer) { parameters, context in + jobQueue.registerJob(id: jobIdentifer) { parameters, context in let runningJobs = runningJobCounter.wrappingIncrementThenLoad(by: 1, ordering: .relaxed) if runningJobs > maxRunningJobCounter.load(ordering: .relaxed) { maxRunningJobCounter.store(runningJobs, ordering: .relaxed) @@ -149,11 +149,11 @@ final class HummingbirdRedisJobsTests: XCTestCase { } func testErrorRetryCount() async throws { - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) let expectation = XCTestExpectation(description: "TestJob.execute was called", expectedFulfillmentCount: 4) struct FailedError: Error {} try await self.testJobQueue(numWorkers: 1) { jobQueue in - jobQueue.registerJob(jobIdentifer, maxRetryCount: 3) { _, _ in + jobQueue.registerJob(id: jobIdentifer, maxRetryCount: 3) { _, _ in expectation.fulfill() throw FailedError() } @@ -176,9 +176,9 @@ final class HummingbirdRedisJobsTests: XCTestCase { let message: String } let expectation = XCTestExpectation(description: "TestJob.execute was called") - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) try await self.testJobQueue(numWorkers: 1) { jobQueue in - jobQueue.registerJob(jobIdentifer) { parameters, _ in + jobQueue.registerJob(id: jobIdentifer) { parameters, _ in XCTAssertEqual(parameters.id, 23) XCTAssertEqual(parameters.message, "Hello!") expectation.fulfill() @@ -191,13 +191,13 @@ final class HummingbirdRedisJobsTests: XCTestCase { /// Test job is cancelled on shutdown func testShutdownJob() async throws { - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) let expectation = XCTestExpectation(description: "TestJob.execute was called", expectedFulfillmentCount: 1) var logger = Logger(label: "HummingbirdJobsTests") logger.logLevel = .trace try await self.testJobQueue(numWorkers: 4) { jobQueue in - jobQueue.registerJob(jobIdentifer) { _, _ in + jobQueue.registerJob(id: jobIdentifer) { _, _ in expectation.fulfill() try await Task.sleep(for: .milliseconds(1000)) } @@ -215,12 +215,12 @@ final class HummingbirdRedisJobsTests: XCTestCase { /// test job fails to decode but queue continues to process func testFailToDecode() async throws { let string: NIOLockedValueBox = .init("") - let jobIdentifer1 = HBJobIdentifier(#function) - let jobIdentifer2 = HBJobIdentifier(#function) + let jobIdentifer1 = JobIdentifier(#function) + let jobIdentifer2 = JobIdentifier(#function) let expectation = XCTestExpectation(description: "job was called", expectedFulfillmentCount: 1) try await self.testJobQueue(numWorkers: 4) { jobQueue in - jobQueue.registerJob(jobIdentifer2) { parameters, _ in + jobQueue.registerJob(id: jobIdentifer2) { parameters, _ in string.withLockedValue { $0 = parameters } expectation.fulfill() } @@ -237,12 +237,12 @@ final class HummingbirdRedisJobsTests: XCTestCase { /// is then rerun on startup of new server func testRerunAtStartup() async throws { struct RetryError: Error {} - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) let firstTime = ManagedAtomic(true) let finished = ManagedAtomic(false) let failedExpectation = XCTestExpectation(description: "TestJob failed", expectedFulfillmentCount: 1) let succeededExpectation = XCTestExpectation(description: "TestJob2 succeeded", expectedFulfillmentCount: 1) - let job = HBJobDefinition(id: jobIdentifer) { _, _ in + let job = JobDefinition(id: jobIdentifer) { _, _ in if firstTime.compareExchange(expected: true, desired: false, ordering: .relaxed).original { failedExpectation.fulfill() throw RetryError() @@ -272,30 +272,30 @@ final class HummingbirdRedisJobsTests: XCTestCase { } func testMultipleJobQueueHandlers() async throws { - let jobIdentifer = HBJobIdentifier(#function) + let jobIdentifer = JobIdentifier(#function) let expectation = XCTestExpectation(description: "TestJob.execute was called", expectedFulfillmentCount: 200) let logger = { var logger = Logger(label: "HummingbirdJobsTests") logger.logLevel = .debug return logger }() - let job = HBJobDefinition(id: jobIdentifer) { parameters, context in + let job = JobDefinition(id: jobIdentifer) { parameters, context in context.logger.info("Parameters=\(parameters)") try await Task.sleep(for: .milliseconds(Int.random(in: 10..<50))) expectation.fulfill() } - let redisService = try HBRedisConnectionPoolService( + let redisService = try RedisConnectionPoolService( .init(hostname: Self.redisHostname, port: 6379), logger: Logger(label: "Redis") ) - let jobQueue = HBJobQueue( - HBRedisQueue(redisService), + let jobQueue = JobQueue( + RedisQueue(redisService), numWorkers: 2, logger: logger ) jobQueue.registerJob(job) - let jobQueue2 = HBJobQueue( - HBRedisQueue(redisService), + let jobQueue2 = JobQueue( + RedisQueue(redisService), numWorkers: 2, logger: logger ) diff --git a/Tests/HummingbirdRedisTests/PersistTests.swift b/Tests/HummingbirdRedisTests/PersistTests.swift index db4a905..deed7e1 100644 --- a/Tests/HummingbirdRedisTests/PersistTests.swift +++ b/Tests/HummingbirdRedisTests/PersistTests.swift @@ -20,15 +20,15 @@ import RediStack import XCTest final class PersistTests: XCTestCase { - static let redisHostname = HBEnvironment.shared.get("REDIS_HOSTNAME") ?? "localhost" + static let redisHostname = Environment.shared.get("REDIS_HOSTNAME") ?? "localhost" - func createApplication(_ updateRouter: (HBRouter, HBPersistDriver) -> Void = { _, _ in }) throws -> some HBApplicationProtocol { - let router = HBRouter() - let redisConnectionPool = try HBRedisConnectionPoolService( + func createApplication(_ updateRouter: (Router, PersistDriver) -> Void = { _, _ in }) throws -> some ApplicationProtocol { + let router = Router() + let redisConnectionPool = try RedisConnectionPoolService( .init(hostname: Self.redisHostname, port: 6379), logger: Logger(label: "Redis") ) - let persist = HBRedisPersistDriver(redisConnectionPoolService: redisConnectionPool) + let persist = RedisPersistDriver(redisConnectionPoolService: redisConnectionPool) router.put("/persist/:tag") { request, context -> HTTPResponse.Status in let buffer = try await request.body.collect(upTo: .max) @@ -37,23 +37,23 @@ final class PersistTests: XCTestCase { return .ok } router.put("/persist/:tag/:time") { request, context -> HTTPResponse.Status in - guard let time = context.parameters.get("time", as: Int.self) else { throw HBHTTPError(.badRequest) } + guard let time = context.parameters.get("time", as: Int.self) else { throw HTTPError(.badRequest) } let buffer = try await request.body.collect(upTo: .max) let tag = try context.parameters.require("tag") try await persist.set(key: tag, value: String(buffer: buffer), expires: .seconds(time)) return .ok } router.get("/persist/:tag") { _, context -> String? in - guard let tag = context.parameters.get("tag", as: String.self) else { throw HBHTTPError(.badRequest) } + guard let tag = context.parameters.get("tag", as: String.self) else { throw HTTPError(.badRequest) } return try await persist.get(key: tag, as: String.self) } router.delete("/persist/:tag") { _, context -> HTTPResponse.Status in - guard let tag = context.parameters.get("tag", as: String.self) else { throw HBHTTPError(.badRequest) } + guard let tag = context.parameters.get("tag", as: String.self) else { throw HTTPError(.badRequest) } try await persist.remove(key: tag) return .noContent } updateRouter(router, persist) - var app = HBApplication(responder: router.buildResponder()) + var app = Application(responder: router.buildResponder()) app.addServices(redisConnectionPool, persist) return app @@ -97,8 +97,8 @@ final class PersistTests: XCTestCase { let tag = try context.parameters.require("tag") do { try await persist.create(key: tag, value: String(buffer: buffer)) - } catch let error as HBPersistError where error == .duplicate { - throw HBHTTPError(.conflict) + } catch let error as PersistError where error == .duplicate { + throw HTTPError(.conflict) } return .ok } @@ -156,13 +156,13 @@ final class PersistTests: XCTestCase { } let app = try self.createApplication { router, persist in router.put("/codable/:tag") { request, context -> HTTPResponse.Status in - guard let tag = context.parameters.get("tag") else { throw HBHTTPError(.badRequest) } + guard let tag = context.parameters.get("tag") else { throw HTTPError(.badRequest) } let buffer = try await request.body.collect(upTo: .max) try await persist.set(key: tag, value: TestCodable(buffer: String(buffer: buffer))) return .ok } router.get("/codable/:tag") { _, context -> String? in - guard let tag = context.parameters.get("tag") else { throw HBHTTPError(.badRequest) } + guard let tag = context.parameters.get("tag") else { throw HTTPError(.badRequest) } let value = try await persist.get(key: tag, as: TestCodable.self) return value?.buffer } diff --git a/Tests/HummingbirdRedisTests/RedisTests.swift b/Tests/HummingbirdRedisTests/RedisTests.swift index 9a6ce9e..acb312a 100644 --- a/Tests/HummingbirdRedisTests/RedisTests.swift +++ b/Tests/HummingbirdRedisTests/RedisTests.swift @@ -20,11 +20,11 @@ import NIOPosix import XCTest final class HummingbirdRedisTests: XCTestCase { - static let env = HBEnvironment() + static let env = Environment() static let redisHostname = env.get("REDIS_HOSTNAME") ?? "localhost" func testApplication() async throws { - let redis = try HBRedisConnectionPoolService( + let redis = try RedisConnectionPoolService( .init(hostname: Self.redisHostname, port: 6379), logger: Logger(label: "Redis") ) @@ -36,15 +36,15 @@ final class HummingbirdRedisTests: XCTestCase { } func testRouteHandler() async throws { - let redis = try HBRedisConnectionPoolService( + let redis = try RedisConnectionPoolService( .init(hostname: Self.redisHostname, port: 6379), logger: Logger(label: "Redis") ) - let router = HBRouter() + let router = Router() router.get("redis") { _, _ in try await redis.send(command: "INFO").map(\.description).get() } - var app = HBApplication(responder: router.buildResponder()) + var app = Application(responder: router.buildResponder()) app.addServices(redis) try await app.test(.live) { client in try await client.execute(uri: "/redis", method: .get) { response in