From 908a1eeac347a7d17f6a0d7529ac7d28af8c97c7 Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Sun, 19 May 2024 20:40:53 +0330 Subject: [PATCH 1/5] Revert "Temporarily don't use `HTTPClient.shared` in `GitHubAPI` (#188)" This reverts commit bf86a019e1ef3e768006fe26df5d4f0978d3099d. --- Lambdas/GitHubAPI/+Client.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lambdas/GitHubAPI/+Client.swift b/Lambdas/GitHubAPI/+Client.swift index d75c16de..2c4be781 100644 --- a/Lambdas/GitHubAPI/+Client.swift +++ b/Lambdas/GitHubAPI/+Client.swift @@ -14,11 +14,7 @@ extension Client { ) let transport = AsyncHTTPClientTransport( configuration: .init( - client: HTTPClient( - configuration: .init( - decompression: .enabled(limit: .ratio(20)) - ) - ), + client: HTTPClient.shared, timeout: timeout ) ) From d7b24c6eb60d35fa30dbaa6687dd1cf569696bbd Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Sun, 19 May 2024 20:42:12 +0330 Subject: [PATCH 2/5] Revert using `HTTPClient.shared` --- Lambdas/AutoFaqs/AutoFaqsHandler.swift | 5 ++- Lambdas/AutoPings/AutoPingsHandler.swift | 5 ++- Lambdas/Faqs/FaqsHandler.swift | 5 ++- .../GHHooks/+Rendering/+LeafRenderer.swift | 13 +++++--- Lambdas/GHHooks/Authenticator.swift | 6 ++-- .../GHHooks/EventHandler/HandlerContext.swift | 5 ++- Lambdas/GHHooks/EventHandler/Requester.swift | 2 +- Lambdas/GHHooks/GHHooksHandler.swift | 16 +++++++-- Lambdas/GHOAuth/OAuthLambda.swift | 10 ++++-- Lambdas/GitHubAPI/+Client.swift | 3 +- Lambdas/Sponsors/SponsorsLambda.swift | 18 +++++----- Lambdas/Users/UsersHandler.swift | 6 ++-- Sources/Penny/+Rendering/+LeafRenderer.swift | 10 ++++-- Sources/Penny/MainService/MainService.swift | 8 +++-- Sources/Penny/MainService/PennyService.swift | 33 ++++++++++++------- Sources/Penny/Penny.swift | 32 ++++++++++++++++-- .../DefaultAutoFaqsService.swift | 5 +-- .../DefaultPingsService.swift | 5 +-- .../DefaultEvolutionService.swift | 2 +- .../FaqsService/DefaultFaqsService.swift | 5 +-- .../DefaultSOService.swift | 2 +- Sources/Rendering/+LeafRenderer.swift | 7 ++-- Sources/Rendering/GHLeafSource.swift | 6 +++- Sources/Shared/ServiceFactory.swift | 4 +-- .../UsersService/DefaultUsersService.swift | 5 +-- Tests/PennyTests/Fake/FakeMainService.swift | 17 +++++++--- Tests/PennyTests/Tests/GHHooksTests.swift | 10 +++++- Tests/PennyTests/Tests/LeafRenderTests.swift | 13 ++++++-- 28 files changed, 186 insertions(+), 72 deletions(-) diff --git a/Lambdas/AutoFaqs/AutoFaqsHandler.swift b/Lambdas/AutoFaqs/AutoFaqsHandler.swift index e350e8b3..62ad872b 100644 --- a/Lambdas/AutoFaqs/AutoFaqsHandler.swift +++ b/Lambdas/AutoFaqs/AutoFaqsHandler.swift @@ -14,7 +14,10 @@ struct AutoFaqsHandler: LambdaHandler { let autoFaqsRepo: S3AutoFaqsRepository init(context: LambdaInitializationContext) async { - self.awsClient = AWSClient() + let awsClient = AWSClient( + httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + ) + self.awsClient = awsClient self.autoFaqsRepo = S3AutoFaqsRepository(awsClient: awsClient, logger: context.logger) } diff --git a/Lambdas/AutoPings/AutoPingsHandler.swift b/Lambdas/AutoPings/AutoPingsHandler.swift index f7176028..49e85351 100644 --- a/Lambdas/AutoPings/AutoPingsHandler.swift +++ b/Lambdas/AutoPings/AutoPingsHandler.swift @@ -14,7 +14,10 @@ struct AutoPingsHandler: LambdaHandler { let pingsRepo: S3AutoPingsRepository init(context: LambdaInitializationContext) async { - self.awsClient = AWSClient() + let awsClient = AWSClient( + httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + ) + self.awsClient = awsClient self.pingsRepo = S3AutoPingsRepository(awsClient: awsClient, logger: context.logger) } diff --git a/Lambdas/Faqs/FaqsHandler.swift b/Lambdas/Faqs/FaqsHandler.swift index 1d5a7e02..86c5435e 100644 --- a/Lambdas/Faqs/FaqsHandler.swift +++ b/Lambdas/Faqs/FaqsHandler.swift @@ -14,7 +14,10 @@ struct FaqsHandler: LambdaHandler { let faqsRepo: S3FaqsRepository init(context: LambdaInitializationContext) async { - self.awsClient = AWSClient() + let awsClient = AWSClient( + httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + ) + self.awsClient = awsClient self.faqsRepo = S3FaqsRepository(awsClient: awsClient, logger: context.logger) } diff --git a/Lambdas/GHHooks/+Rendering/+LeafRenderer.swift b/Lambdas/GHHooks/+Rendering/+LeafRenderer.swift index 6d975b26..570f2c01 100644 --- a/Lambdas/GHHooks/+Rendering/+LeafRenderer.swift +++ b/Lambdas/GHHooks/+Rendering/+LeafRenderer.swift @@ -5,11 +5,16 @@ import Logging import Foundation extension LeafRenderer { - static func forGHHooks(logger: Logger) throws -> LeafRenderer { + static func forGHHooks(httpClient: HTTPClient, logger: Logger) throws -> LeafRenderer { try LeafRenderer( subDirectory: "GHHooksLambda", - extraSources: [DocsLeafSource(logger: logger)], - logger: logger + httpClient: httpClient, + extraSources: [DocsLeafSource( + httpClient: httpClient, + logger: logger + )], + logger: logger, + on: httpClient.eventLoopGroup.next() ) } } @@ -39,7 +44,7 @@ private struct DocsLeafSource: LeafSource { } } - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let logger: Logger func file( diff --git a/Lambdas/GHHooks/Authenticator.swift b/Lambdas/GHHooks/Authenticator.swift index 20bc54b6..88120dcc 100644 --- a/Lambdas/GHHooks/Authenticator.swift +++ b/Lambdas/GHHooks/Authenticator.swift @@ -10,7 +10,7 @@ import Shared actor Authenticator { private let secretsRetriever: SecretsRetriever - private let httpClient: HTTPClient = .shared + private let httpClient: HTTPClient let logger: Logger /// The cached access token. @@ -18,8 +18,9 @@ actor Authenticator { private let queue = SerialProcessor() - init(secretsRetriever: SecretsRetriever, logger: Logger) { + init(secretsRetriever: SecretsRetriever, httpClient: HTTPClient, logger: Logger) { self.secretsRetriever = secretsRetriever + self.httpClient = httpClient self.logger = logger } @@ -60,6 +61,7 @@ actor Authenticator { private func makeClient(token: String) throws -> Client { try .makeForGitHub( + httpClient: self.httpClient, authorization: .bearer(token), logger: self.logger ) diff --git a/Lambdas/GHHooks/EventHandler/HandlerContext.swift b/Lambdas/GHHooks/EventHandler/HandlerContext.swift index 080a6246..7dd8b0a8 100644 --- a/Lambdas/GHHooks/EventHandler/HandlerContext.swift +++ b/Lambdas/GHHooks/EventHandler/HandlerContext.swift @@ -9,7 +9,7 @@ import Logging struct HandlerContext: Sendable { let eventName: GHEvent.Kind let event: GHEvent - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let discordClient: any DiscordClient let githubClient: Client let renderClient: RenderClient @@ -21,6 +21,7 @@ struct HandlerContext: Sendable { init( eventName: GHEvent.Kind, event: GHEvent, + httpClient: HTTPClient, discordClient: any DiscordClient, githubClient: Client, renderClient: RenderClient, @@ -30,6 +31,7 @@ struct HandlerContext: Sendable { ) { self.eventName = eventName self.event = event + self.httpClient = httpClient self.discordClient = discordClient self.githubClient = githubClient self.renderClient = renderClient @@ -38,6 +40,7 @@ struct HandlerContext: Sendable { self.requester = .init( eventName: eventName, event: event, + httpClient: httpClient, discordClient: discordClient, githubClient: githubClient, usersService: usersService, diff --git a/Lambdas/GHHooks/EventHandler/Requester.swift b/Lambdas/GHHooks/EventHandler/Requester.swift index b9352026..646ed5bf 100644 --- a/Lambdas/GHHooks/EventHandler/Requester.swift +++ b/Lambdas/GHHooks/EventHandler/Requester.swift @@ -8,7 +8,7 @@ import Logging struct Requester: Sendable { let eventName: GHEvent.Kind let event: GHEvent - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let discordClient: any DiscordClient let githubClient: Client let usersService: any UsersService diff --git a/Lambdas/GHHooks/GHHooksHandler.swift b/Lambdas/GHHooks/GHHooksHandler.swift index f5fb499d..3f60b1c5 100644 --- a/Lambdas/GHHooks/GHHooksHandler.swift +++ b/Lambdas/GHHooks/GHHooksHandler.swift @@ -16,6 +16,7 @@ struct GHHooksHandler: LambdaHandler { typealias Event = APIGatewayV2Request typealias Output = APIGatewayV2Response + let httpClient: HTTPClient let githubClient: Client let secretsRetriever: SecretsRetriever let messageLookupRepo: any MessageLookupRepo @@ -26,7 +27,7 @@ struct GHHooksHandler: LambdaHandler { var discordClient: any DiscordClient { get async throws { let botToken = try await secretsRetriever.getSecret(arnEnvVarKey: "BOT_TOKEN_ARN") - return await DefaultDiscordClient(token: botToken) + return await DefaultDiscordClient(httpClient: httpClient, token: botToken) } } @@ -36,15 +37,19 @@ struct GHHooksHandler: LambdaHandler { /// bootstrapping the logging system which it appears to not have. DiscordGlobalConfiguration.makeLogger = { _ in context.logger } - let awsClient = AWSClient() + self.httpClient = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) + + let awsClient = AWSClient(httpClientProvider: .shared(self.httpClient)) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: logger) let authenticator = Authenticator( secretsRetriever: secretsRetriever, + httpClient: httpClient, logger: logger ) self.githubClient = try .makeForGitHub( + httpClient: httpClient, authorization: .computedBearer { isRetry in try await authenticator.generateAccessToken( forceRefreshToken: isRetry @@ -130,15 +135,20 @@ struct GHHooksHandler: LambdaHandler { context: .init( eventName: eventName, event: event, + httpClient: httpClient, discordClient: discordClient, githubClient: githubClient, renderClient: RenderClient( renderer: try .forGHHooks( + httpClient: httpClient, logger: logger ) ), messageLookupRepo: self.messageLookupRepo, - usersService: ServiceFactory.makeUsersService(apiBaseURL: apiBaseURL), + usersService: ServiceFactory.makeUsersService( + httpClient: httpClient, + apiBaseURL: apiBaseURL + ), logger: logger ) ).handle() diff --git a/Lambdas/GHOAuth/OAuthLambda.swift b/Lambdas/GHOAuth/OAuthLambda.swift index 6eee5ea5..cfcc5dbc 100644 --- a/Lambdas/GHOAuth/OAuthLambda.swift +++ b/Lambdas/GHOAuth/OAuthLambda.swift @@ -19,7 +19,7 @@ struct GHOAuthHandler: LambdaHandler { typealias Event = APIGatewayV2Request typealias Output = APIGatewayV2Response - let client: HTTPClient = .shared + let client: HTTPClient let secretsRetriever: SecretsRetriever let userService: any UsersService let signers: JWTSigners @@ -38,13 +38,17 @@ struct GHOAuthHandler: LambdaHandler { } init(context: LambdaInitializationContext) async throws { + self.client = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) self.logger = context.logger - let awsClient = AWSClient() + let awsClient = AWSClient(httpClientProvider: .shared(client)) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: logger) let apiBaseURL = try requireEnvVar("API_BASE_URL") - self.userService = ServiceFactory.makeUsersService(apiBaseURL: apiBaseURL) + self.userService = ServiceFactory.makeUsersService( + httpClient: client, + apiBaseURL: apiBaseURL + ) signers = JWTSigners() signers.use(.es256(key: try getJWTSignersPublicKey())) diff --git a/Lambdas/GitHubAPI/+Client.swift b/Lambdas/GitHubAPI/+Client.swift index 2c4be781..cba51e21 100644 --- a/Lambdas/GitHubAPI/+Client.swift +++ b/Lambdas/GitHubAPI/+Client.swift @@ -4,6 +4,7 @@ import struct NIOCore.TimeAmount extension Client { package static func makeForGitHub( + httpClient: HTTPClient, authorization: AuthorizationHeader, timeout: TimeAmount = .seconds(5), logger: Logger @@ -14,7 +15,7 @@ extension Client { ) let transport = AsyncHTTPClientTransport( configuration: .init( - client: HTTPClient.shared, + client: httpClient, timeout: timeout ) ) diff --git a/Lambdas/Sponsors/SponsorsLambda.swift b/Lambdas/Sponsors/SponsorsLambda.swift index a1235be9..3e80f19d 100644 --- a/Lambdas/Sponsors/SponsorsLambda.swift +++ b/Lambdas/Sponsors/SponsorsLambda.swift @@ -13,8 +13,8 @@ struct SponsorsHandler: LambdaHandler { typealias Event = APIGatewayV2Request typealias Output = APIGatewayV2Response - let httpClient: HTTPClient = .shared - let awsClient: AWSClient = AWSClient() + let httpClient: HTTPClient + let awsClient: AWSClient let secretsRetriever: SecretsRetriever let logger: Logger @@ -23,7 +23,7 @@ struct SponsorsHandler: LambdaHandler { var discordClient: any DiscordClient { get async throws { let botToken = try await secretsRetriever.getSecret(arnEnvVarKey: "BOT_TOKEN_ARN") - return await DefaultDiscordClient(token: botToken) + return await DefaultDiscordClient(httpClient: httpClient, token: botToken) } } @@ -32,6 +32,8 @@ struct SponsorsHandler: LambdaHandler { } init(context: LambdaInitializationContext) async throws { + self.httpClient = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) + self.awsClient = AWSClient(httpClientProvider: .shared(httpClient)) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: context.logger) self.logger = context.logger } @@ -61,7 +63,10 @@ struct SponsorsHandler: LambdaHandler { context.logger.debug("Looking for user in the DB") let newSponsorID = payload.sender.id let apiBaseURL = try requireEnvVar("API_BASE_URL") - let userService = ServiceFactory.makeUsersService(apiBaseURL: apiBaseURL) + let userService = ServiceFactory.makeUsersService( + httpClient: httpClient, + apiBaseURL: apiBaseURL + ) guard let user = try await userService.getUser(githubID: "\(newSponsorID)") else { context.logger.error("No user found with GitHub ID \(newSponsorID)") return APIGatewayV2Response( @@ -226,10 +231,7 @@ struct SponsorsHandler: LambdaHandler { triggerActionRequest.body = .bytes(ByteBuffer(string: #"{"ref":"main"}"#)) // Send request to trigger workflow and read response - let githubResponse = try await httpClient.execute( - triggerActionRequest, - timeout: .seconds(10) - ) + let githubResponse = try await httpClient.execute(triggerActionRequest, timeout: .seconds(10)) guard 200..<300 ~= githubResponse.status.code else { let body = try await githubResponse.body.collect(upTo: 1024 * 1024) diff --git a/Lambdas/Users/UsersHandler.swift b/Lambdas/Users/UsersHandler.swift index acd139bf..11bf5653 100644 --- a/Lambdas/Users/UsersHandler.swift +++ b/Lambdas/Users/UsersHandler.swift @@ -14,10 +14,10 @@ struct UsersHandler: LambdaHandler { let logger: Logger init(context: LambdaInitializationContext) async { - self.internalService = InternalUsersService( - awsClient: AWSClient(), - logger: context.logger + let awsClient = AWSClient( + httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) ) + self.internalService = InternalUsersService(awsClient: awsClient, logger: context.logger) self.logger = context.logger } diff --git a/Sources/Penny/+Rendering/+LeafRenderer.swift b/Sources/Penny/+Rendering/+LeafRenderer.swift index b97e841b..ba32ce7c 100644 --- a/Sources/Penny/+Rendering/+LeafRenderer.swift +++ b/Sources/Penny/+Rendering/+LeafRenderer.swift @@ -5,10 +5,16 @@ import Logging import Foundation extension LeafRenderer { - static func forPenny(logger: Logger) throws -> LeafRenderer { + static func forPenny( + httpClient: HTTPClient, + logger: Logger, + on eventLoop: any EventLoop + ) throws -> LeafRenderer { try LeafRenderer( subDirectory: "Penny", - logger: logger + httpClient: httpClient, + logger: logger, + on: eventLoop ) } } diff --git a/Sources/Penny/MainService/MainService.swift b/Sources/Penny/MainService/MainService.swift index 53480c68..a8744a38 100644 --- a/Sources/Penny/MainService/MainService.swift +++ b/Sources/Penny/MainService/MainService.swift @@ -4,12 +4,14 @@ import AsyncHTTPClient import NIOCore protocol MainService: Sendable { - func bootstrapLoggingSystem() async throws - func makeBot() async throws -> any GatewayManager + func bootstrapLoggingSystem(httpClient: HTTPClient) async throws + func makeBot(httpClient: HTTPClient) async throws -> any GatewayManager func makeCache(bot: any GatewayManager) async throws -> DiscordCache func beforeConnectCall( bot: any GatewayManager, - cache: DiscordCache + cache: DiscordCache, + httpClient: HTTPClient, + awsClient: AWSClient ) async throws -> HandlerContext func afterConnectCall(context: HandlerContext) async throws } diff --git a/Sources/Penny/MainService/PennyService.swift b/Sources/Penny/MainService/PennyService.swift index 94973e46..717b2ecd 100644 --- a/Sources/Penny/MainService/PennyService.swift +++ b/Sources/Penny/MainService/PennyService.swift @@ -8,7 +8,7 @@ import Logging import NIOPosix struct PennyService: MainService { - func bootstrapLoggingSystem() async throws { + func bootstrapLoggingSystem(httpClient: HTTPClient) async throws { #if DEBUG // Discord-logging is disabled in debug based on the logger configuration, // so we can just use an invalid url @@ -17,6 +17,7 @@ struct PennyService: MainService { let webhookURL = Constants.loggingWebhookURL #endif DiscordGlobalConfiguration.logManager = await DiscordLogManager( + httpClient: httpClient, configuration: .init( aliveNotice: .init( address: try! .url(webhookURL), @@ -24,7 +25,7 @@ struct PennyService: MainService { message: "I'm Alive! :)", initialNoticeMention: .user(Constants.botDevUserId) ), - sendFullLogsAsAttachment: .enabled, + sendFullLogAsAttachment: .enabled, mentions: [ .warning: .user(Constants.botDevUserId), .error: .user(Constants.botDevUserId), @@ -47,7 +48,7 @@ struct PennyService: MainService { ) } - func makeBot() async throws -> any GatewayManager { + func makeBot(httpClient: HTTPClient) async throws -> any GatewayManager { /// Custom caching for the `getApplicationGlobalCommands` endpoint. let clientConfiguration = ClientConfiguration( cachingBehavior: .custom( @@ -58,6 +59,8 @@ struct PennyService: MainService { ) ) return await BotGatewayManager( + eventLoopGroup: MultiThreadedEventLoopGroup.singleton, + httpClient: httpClient, clientConfiguration: clientConfiguration, token: Constants.botToken, presence: .init( @@ -87,15 +90,19 @@ struct PennyService: MainService { func beforeConnectCall( bot: any GatewayManager, - cache: DiscordCache + cache: DiscordCache, + httpClient: HTTPClient, + awsClient: AWSClient ) async throws -> HandlerContext { - let awsClient = AWSClient() - let usersService = ServiceFactory.makeUsersService(apiBaseURL: Constants.apiBaseURL) - let pingsService = DefaultPingsService() - let faqsService = DefaultFaqsService() - let autoFaqsService = DefaultAutoFaqsService() - let evolutionService = DefaultEvolutionService() - let soService = DefaultSOService() + let usersService = ServiceFactory.makeUsersService( + httpClient: httpClient, + apiBaseURL: Constants.apiBaseURL + ) + let pingsService = DefaultPingsService(httpClient: httpClient) + let faqsService = DefaultFaqsService(httpClient: httpClient) + let autoFaqsService = DefaultAutoFaqsService(httpClient: httpClient) + let evolutionService = DefaultEvolutionService(httpClient: httpClient) + let soService = DefaultSOService(httpClient: httpClient) let discordService = DiscordService(discordClient: bot.client, cache: cache) let evolutionChecker = EvolutionChecker( evolutionService: evolutionService, @@ -124,7 +131,9 @@ struct PennyService: MainService { discordService: discordService, renderClient: .init( renderer: try .forPenny( - logger: Logger(label: "Penny+Leaf") + httpClient: httpClient, + logger: Logger(label: "Penny+Leaf"), + on: httpClient.eventLoopGroup.next() ) ), evolutionChecker: evolutionChecker, diff --git a/Sources/Penny/Penny.swift b/Sources/Penny/Penny.swift index caf27004..3f1d0843 100644 --- a/Sources/Penny/Penny.swift +++ b/Sources/Penny/Penny.swift @@ -9,12 +9,38 @@ struct Penny { } static func start(mainService: any MainService) async throws { - try await mainService.bootstrapLoggingSystem() + /// Use `1` instead of `System.coreCount`. + /// This is preferred for apps that primarily use structured concurrency. + let httpClient = HTTPClient( + eventLoopGroup: MultiThreadedEventLoopGroup.singleton, + configuration: .init( + decompression: .enabled( + limit: .size(1 << 30) /// 1 GiB + ) + ) + ) + let awsClient = AWSClient(httpClientProvider: .shared(httpClient)) - let bot = try await mainService.makeBot() + /// These shutdown calls are only useful for tests where we call `Penny.main()` repeatedly + defer { + /// Shutdown in reverse order of dependance. + try! awsClient.syncShutdown() + try! httpClient.syncShutdown() + } + + try await mainService.bootstrapLoggingSystem(httpClient: httpClient) + + let bot = try await mainService.makeBot( + httpClient: httpClient + ) let cache = try await mainService.makeCache(bot: bot) - let context = try await mainService.beforeConnectCall(bot: bot, cache: cache) + let context = try await mainService.beforeConnectCall( + bot: bot, + cache: cache, + httpClient: httpClient, + awsClient: awsClient + ) await bot.connect() diff --git a/Sources/Penny/Services/AutoFaqsService/DefaultAutoFaqsService.swift b/Sources/Penny/Services/AutoFaqsService/DefaultAutoFaqsService.swift index 7d4f38e2..ff82f750 100644 --- a/Sources/Penny/Services/AutoFaqsService/DefaultAutoFaqsService.swift +++ b/Sources/Penny/Services/AutoFaqsService/DefaultAutoFaqsService.swift @@ -49,7 +49,7 @@ actor DefaultAutoFaqsService: AutoFaqsService { } } - let httpClient: HTTPClient = .shared + var httpClient: HTTPClient! var logger = Logger(label: "DefaultAutoFaqsService") /// Use `getAll()` to retrieve. @@ -67,7 +67,8 @@ actor DefaultAutoFaqsService: AutoFaqsService { let decoder = JSONDecoder() let encoder = JSONEncoder() - init() { + init(httpClient: HTTPClient) { + self.httpClient = httpClient Task { await self.setUpResetItemsTask() await self.getFreshItemsForCache() diff --git a/Sources/Penny/Services/AutoPingsService/DefaultPingsService.swift b/Sources/Penny/Services/AutoPingsService/DefaultPingsService.swift index 21f6ee1e..97864999 100644 --- a/Sources/Penny/Services/AutoPingsService/DefaultPingsService.swift +++ b/Sources/Penny/Services/AutoPingsService/DefaultPingsService.swift @@ -11,7 +11,7 @@ import NIOHTTP1 actor DefaultPingsService: AutoPingsService { - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient var logger = Logger(label: "DefaultPingsService") /// Use `getAll()` to retrieve. @@ -23,7 +23,8 @@ actor DefaultPingsService: AutoPingsService { let decoder = JSONDecoder() let encoder = JSONEncoder() - init() { + init(httpClient: HTTPClient) { + self.httpClient = httpClient Task { await self.setUpResetItemsTask() await self.getFreshItemsForCache() diff --git a/Sources/Penny/Services/EvolutionService/DefaultEvolutionService.swift b/Sources/Penny/Services/EvolutionService/DefaultEvolutionService.swift index 75b556c0..349d9b00 100644 --- a/Sources/Penny/Services/EvolutionService/DefaultEvolutionService.swift +++ b/Sources/Penny/Services/EvolutionService/DefaultEvolutionService.swift @@ -20,7 +20,7 @@ struct DefaultEvolutionService: EvolutionService { } } - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let decoder = JSONDecoder() func list() async throws -> [Proposal] { diff --git a/Sources/Penny/Services/FaqsService/DefaultFaqsService.swift b/Sources/Penny/Services/FaqsService/DefaultFaqsService.swift index 960f98ee..885a7fdb 100644 --- a/Sources/Penny/Services/FaqsService/DefaultFaqsService.swift +++ b/Sources/Penny/Services/FaqsService/DefaultFaqsService.swift @@ -10,7 +10,7 @@ import NIOHTTP1 actor DefaultFaqsService: FaqsService { - let httpClient: HTTPClient = .shared + var httpClient: HTTPClient! var logger = Logger(label: "DefaultFaqsService") /// Use `getAll()` to retrieve. @@ -23,7 +23,8 @@ actor DefaultFaqsService: FaqsService { let decoder = JSONDecoder() let encoder = JSONEncoder() - init() { + init(httpClient: HTTPClient) { + self.httpClient = httpClient Task { await self.setUpResetItemsTask() await self.getFreshItemsForCache() diff --git a/Sources/Penny/Services/StackoverflowService /DefaultSOService.swift b/Sources/Penny/Services/StackoverflowService /DefaultSOService.swift index fb71f925..bdaafb69 100644 --- a/Sources/Penny/Services/StackoverflowService /DefaultSOService.swift +++ b/Sources/Penny/Services/StackoverflowService /DefaultSOService.swift @@ -4,7 +4,7 @@ import Logging import Foundation struct DefaultSOService: SOService { - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let logger = Logger(label: "DefaultSOService") let decoder = JSONDecoder() private static let urlEncodedAPIKey = Constants.StackOverflow.apiKey diff --git a/Sources/Rendering/+LeafRenderer.swift b/Sources/Rendering/+LeafRenderer.swift index cf148b3e..cffb3b14 100644 --- a/Sources/Rendering/+LeafRenderer.swift +++ b/Sources/Rendering/+LeafRenderer.swift @@ -7,8 +7,10 @@ import Foundation extension LeafRenderer { package convenience init( subDirectory: String, + httpClient: HTTPClient, extraSources: [any LeafSource] = [], - logger: Logger + logger: Logger, + on eventLoop: any EventLoop ) throws { let path = "Templates/\(subDirectory)" let workingDirectory = FileManager.default.currentDirectoryPath @@ -23,6 +25,7 @@ extension LeafRenderer { ) let ghSource = GHLeafSource( path: path, + httpClient: httpClient, logger: logger ) let sources = LeafSources() @@ -37,7 +40,7 @@ extension LeafRenderer { configuration: configuration, tags: tags, sources: sources, - eventLoop: HTTPClient.shared.eventLoopGroup.next() + eventLoop: eventLoop ) } } diff --git a/Sources/Rendering/GHLeafSource.swift b/Sources/Rendering/GHLeafSource.swift index bee48f62..f683a8b7 100644 --- a/Sources/Rendering/GHLeafSource.swift +++ b/Sources/Rendering/GHLeafSource.swift @@ -19,16 +19,18 @@ struct GHLeafSource: LeafSource { private actor ActorGHLeafSource { let path: String - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let logger: Logger let queue = SerialProcessor() var cache: [String: ByteBuffer] = [:] init( path: String, + httpClient: HTTPClient, logger: Logger ) { self.path = path + self.httpClient = httpClient self.logger = logger } @@ -68,10 +70,12 @@ struct GHLeafSource: LeafSource { init( path: String, + httpClient: HTTPClient, logger: Logger ) { self.underlying = .init( path: path, + httpClient: httpClient, logger: logger ) } diff --git a/Sources/Shared/ServiceFactory.swift b/Sources/Shared/ServiceFactory.swift index 8d910a79..6e99737e 100644 --- a/Sources/Shared/ServiceFactory.swift +++ b/Sources/Shared/ServiceFactory.swift @@ -1,7 +1,7 @@ import AsyncHTTPClient package enum ServiceFactory { - package static func makeUsersService(apiBaseURL: String) -> any UsersService { - DefaultUsersService(apiBaseURL: apiBaseURL) + package static func makeUsersService(httpClient: HTTPClient, apiBaseURL: String) -> any UsersService { + DefaultUsersService(httpClient: httpClient, apiBaseURL: apiBaseURL) } } diff --git a/Sources/Shared/UsersService/DefaultUsersService.swift b/Sources/Shared/UsersService/DefaultUsersService.swift index a2397537..a76c0119 100644 --- a/Sources/Shared/UsersService/DefaultUsersService.swift +++ b/Sources/Shared/UsersService/DefaultUsersService.swift @@ -9,14 +9,15 @@ import DiscordModels import Models struct DefaultUsersService: UsersService { - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let apiBaseURL: String let logger = Logger(label: "DefaultUsersService") let decoder = JSONDecoder() let encoder = JSONEncoder() - init(apiBaseURL: String) { + init(httpClient: HTTPClient, apiBaseURL: String) { + self.httpClient = httpClient self.apiBaseURL = apiBaseURL } diff --git a/Tests/PennyTests/Fake/FakeMainService.swift b/Tests/PennyTests/Fake/FakeMainService.swift index 8f43da9e..2aa7fd4c 100644 --- a/Tests/PennyTests/Fake/FakeMainService.swift +++ b/Tests/PennyTests/Fake/FakeMainService.swift @@ -11,7 +11,7 @@ import XCTest actor FakeMainService: MainService { let manager: FakeManager let cache: DiscordCache - let httpClient: HTTPClient = .shared + let httpClient: HTTPClient let context: HandlerContext var botStateManager: BotStateManager { context.botStateManager @@ -27,6 +27,9 @@ actor FakeMainService: MainService { requestAllMembers: .enabled, storage: cacheStorage ) + self.httpClient = HTTPClient( + eventLoopGroup: MultiThreadedEventLoopGroup.singleton + ) self.context = try Self.makeContext( manager: manager, cache: cache, @@ -38,9 +41,9 @@ actor FakeMainService: MainService { try! httpClient.syncShutdown() } - func bootstrapLoggingSystem() async throws { } + func bootstrapLoggingSystem(httpClient: HTTPClient) async throws { } - func makeBot() async throws -> any GatewayManager { + func makeBot(httpClient: HTTPClient) async throws -> any GatewayManager { return manager } @@ -50,7 +53,9 @@ actor FakeMainService: MainService { func beforeConnectCall( bot: any GatewayManager, - cache: DiscordCache + cache: DiscordCache, + httpClient: HTTPClient, + awsClient: AWSClient ) async throws -> HandlerContext { await context.botStateManager.start(onStarted: { }) return context @@ -92,7 +97,9 @@ actor FakeMainService: MainService { discordService: discordService, renderClient: .init( renderer: try .forPenny( - logger: Logger(label: "Tests_Penny+Leaf+FakeService") + httpClient: httpClient, + logger: Logger(label: "Tests_Penny+Leaf+FakeService"), + on: httpClient.eventLoopGroup.next() ) ), evolutionChecker: evolutionChecker, diff --git a/Tests/PennyTests/Tests/GHHooksTests.swift b/Tests/PennyTests/Tests/GHHooksTests.swift index 4ba52499..beab2283 100644 --- a/Tests/PennyTests/Tests/GHHooksTests.swift +++ b/Tests/PennyTests/Tests/GHHooksTests.swift @@ -13,7 +13,9 @@ import NIOPosix import XCTest class GHHooksTests: XCTestCase { - let httpClient = HTTPClient.shared + let httpClient = HTTPClient( + eventLoopGroup: MultiThreadedEventLoopGroup.singleton + ) let decoder: JSONDecoder = { var decoder = JSONDecoder() @@ -28,6 +30,10 @@ class GHHooksTests: XCTestCase { FakeResponseStorage.shared = FakeResponseStorage() } + override func tearDown() { + try! httpClient.syncShutdown() + } + func testUnicodesPrefix() throws { do { let scalars_16 = "Hello, world! 👍🏾" @@ -897,6 +903,7 @@ class GHHooksTests: XCTestCase { return HandlerContext( eventName: eventName, event: event, + httpClient: httpClient, discordClient: FakeDiscordClient(), githubClient: Client( serverURL: try Servers.server1(), @@ -904,6 +911,7 @@ class GHHooksTests: XCTestCase { ), renderClient: RenderClient( renderer: try .forGHHooks( + httpClient: httpClient, logger: logger ) ), diff --git a/Tests/PennyTests/Tests/LeafRenderTests.swift b/Tests/PennyTests/Tests/LeafRenderTests.swift index fd2f28a8..90f1f216 100644 --- a/Tests/PennyTests/Tests/LeafRenderTests.swift +++ b/Tests/PennyTests/Tests/LeafRenderTests.swift @@ -10,17 +10,22 @@ import NIOPosix import XCTest class LeafRenderTests: XCTestCase { - let httpClient = HTTPClient.shared + let httpClient = HTTPClient( + eventLoopGroup: MultiThreadedEventLoopGroup.singleton + ) lazy var ghHooksRenderClient = RenderClient( renderer: try! .forGHHooks( + httpClient: httpClient, logger: Logger(label: "RenderClientGHHooksTests") ) ) lazy var pennyRenderClient = RenderClient( renderer: try! .forPenny( - logger: Logger(label: "Tests_Penny+LeafRendering") + httpClient: httpClient, + logger: Logger(label: "Tests_Penny+LeafRendering"), + on: httpClient.eventLoopGroup.next() ) ) @@ -28,6 +33,10 @@ class LeafRenderTests: XCTestCase { FakeResponseStorage.shared = FakeResponseStorage() } + override func tearDown() { + try! httpClient.syncShutdown() + } + func testTranslationNeededDescription() async throws { let rendered = try await ghHooksRenderClient.translationNeededDescription(number: 1) XCTAssertGreaterThan(rendered.count, 20) From 65c4d5836e0ca39fafb91127bedfacf0e13d02f2 Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Sun, 19 May 2024 20:59:01 +0330 Subject: [PATCH 3/5] use a new shared configuration for Penny's HTTPClient's --- Lambdas/AutoFaqs/AutoFaqsHandler.swift | 11 +++++++---- Lambdas/AutoPings/AutoPingsHandler.swift | 11 +++++++---- Lambdas/Faqs/FaqsHandler.swift | 7 +++++-- Lambdas/GHHooks/GHHooksHandler.swift | 7 +++++-- Lambdas/GHOAuth/OAuthLambda.swift | 7 +++++-- Lambdas/Sponsors/SponsorsLambda.swift | 7 +++++-- Lambdas/Users/UsersHandler.swift | 8 ++++++-- Sources/Penny/MainService/PennyService.swift | 2 +- Sources/Penny/Penny.swift | 9 +++------ Sources/Shared/+HTTPClient.swift | 17 +++++++++++++++++ Tests/PennyTests/Fake/FakeMainService.swift | 3 ++- Tests/PennyTests/Tests/GHHooksTests.swift | 3 ++- Tests/PennyTests/Tests/LeafRenderTests.swift | 3 ++- 13 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 Sources/Shared/+HTTPClient.swift diff --git a/Lambdas/AutoFaqs/AutoFaqsHandler.swift b/Lambdas/AutoFaqs/AutoFaqsHandler.swift index 62ad872b..530aae6f 100644 --- a/Lambdas/AutoFaqs/AutoFaqsHandler.swift +++ b/Lambdas/AutoFaqs/AutoFaqsHandler.swift @@ -1,8 +1,10 @@ import AWSLambdaRuntime import AWSLambdaEvents +import AsyncHTTPClient import Foundation import SotoCore import Models +import Shared import LambdasShared @main @@ -14,11 +16,12 @@ struct AutoFaqsHandler: LambdaHandler { let autoFaqsRepo: S3AutoFaqsRepository init(context: LambdaInitializationContext) async { - let awsClient = AWSClient( - httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + let httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny ) - self.awsClient = awsClient - self.autoFaqsRepo = S3AutoFaqsRepository(awsClient: awsClient, logger: context.logger) + self.awsClient = AWSClient(httpClient: httpClient) + self.autoFaqsRepo = S3AutoFaqsRepository(awsClient: self.awsClient, logger: context.logger) } func handle( diff --git a/Lambdas/AutoPings/AutoPingsHandler.swift b/Lambdas/AutoPings/AutoPingsHandler.swift index 49e85351..a9818dce 100644 --- a/Lambdas/AutoPings/AutoPingsHandler.swift +++ b/Lambdas/AutoPings/AutoPingsHandler.swift @@ -1,8 +1,10 @@ import AWSLambdaRuntime import AWSLambdaEvents +import AsyncHTTPClient import Foundation import SotoCore import Models +import Shared import LambdasShared @main @@ -14,11 +16,12 @@ struct AutoPingsHandler: LambdaHandler { let pingsRepo: S3AutoPingsRepository init(context: LambdaInitializationContext) async { - let awsClient = AWSClient( - httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + let httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny ) - self.awsClient = awsClient - self.pingsRepo = S3AutoPingsRepository(awsClient: awsClient, logger: context.logger) + self.awsClient = AWSClient(httpClient: httpClient) + self.pingsRepo = S3AutoPingsRepository(awsClient: self.awsClient, logger: context.logger) } func handle( diff --git a/Lambdas/Faqs/FaqsHandler.swift b/Lambdas/Faqs/FaqsHandler.swift index 86c5435e..eb9babd3 100644 --- a/Lambdas/Faqs/FaqsHandler.swift +++ b/Lambdas/Faqs/FaqsHandler.swift @@ -1,5 +1,6 @@ import AWSLambdaRuntime import AWSLambdaEvents +import AsyncHTTPClient import Foundation import SotoCore import Models @@ -14,9 +15,11 @@ struct FaqsHandler: LambdaHandler { let faqsRepo: S3FaqsRepository init(context: LambdaInitializationContext) async { - let awsClient = AWSClient( - httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + let httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny ) + let awsClient = AWSClient(httpClient: httpClient) self.awsClient = awsClient self.faqsRepo = S3FaqsRepository(awsClient: awsClient, logger: context.logger) } diff --git a/Lambdas/GHHooks/GHHooksHandler.swift b/Lambdas/GHHooks/GHHooksHandler.swift index 3f60b1c5..69c85318 100644 --- a/Lambdas/GHHooks/GHHooksHandler.swift +++ b/Lambdas/GHHooks/GHHooksHandler.swift @@ -37,9 +37,12 @@ struct GHHooksHandler: LambdaHandler { /// bootstrapping the logging system which it appears to not have. DiscordGlobalConfiguration.makeLogger = { _ in context.logger } - self.httpClient = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) + self.httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny + ) - let awsClient = AWSClient(httpClientProvider: .shared(self.httpClient)) + let awsClient = AWSClient(httpClient: self.httpClient) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: logger) let authenticator = Authenticator( diff --git a/Lambdas/GHOAuth/OAuthLambda.swift b/Lambdas/GHOAuth/OAuthLambda.swift index cfcc5dbc..ba629cc2 100644 --- a/Lambdas/GHOAuth/OAuthLambda.swift +++ b/Lambdas/GHOAuth/OAuthLambda.swift @@ -38,10 +38,13 @@ struct GHOAuthHandler: LambdaHandler { } init(context: LambdaInitializationContext) async throws { - self.client = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) + self.client = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny + ) self.logger = context.logger - let awsClient = AWSClient(httpClientProvider: .shared(client)) + let awsClient = AWSClient(httpClient: client) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: logger) let apiBaseURL = try requireEnvVar("API_BASE_URL") diff --git a/Lambdas/Sponsors/SponsorsLambda.swift b/Lambdas/Sponsors/SponsorsLambda.swift index 3e80f19d..755e6282 100644 --- a/Lambdas/Sponsors/SponsorsLambda.swift +++ b/Lambdas/Sponsors/SponsorsLambda.swift @@ -32,8 +32,11 @@ struct SponsorsHandler: LambdaHandler { } init(context: LambdaInitializationContext) async throws { - self.httpClient = HTTPClient(eventLoopGroupProvider: .shared(context.eventLoop)) - self.awsClient = AWSClient(httpClientProvider: .shared(httpClient)) + self.httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny + ) + self.awsClient = AWSClient(httpClient: self.httpClient) self.secretsRetriever = SecretsRetriever(awsClient: awsClient, logger: context.logger) self.logger = context.logger } diff --git a/Lambdas/Users/UsersHandler.swift b/Lambdas/Users/UsersHandler.swift index 11bf5653..fb806eef 100644 --- a/Lambdas/Users/UsersHandler.swift +++ b/Lambdas/Users/UsersHandler.swift @@ -1,8 +1,10 @@ import AWSLambdaRuntime import AWSLambdaEvents +import AsyncHTTPClient import Foundation import SotoCore import Models +import Shared import LambdasShared @main @@ -14,9 +16,11 @@ struct UsersHandler: LambdaHandler { let logger: Logger init(context: LambdaInitializationContext) async { - let awsClient = AWSClient( - httpClientProvider: .createNewWithEventLoopGroup(context.eventLoop) + let httpClient = HTTPClient( + eventLoopGroupProvider: .shared(context.eventLoop), + configuration: .forPenny ) + let awsClient = AWSClient(httpClient: httpClient) self.internalService = InternalUsersService(awsClient: awsClient, logger: context.logger) self.logger = context.logger } diff --git a/Sources/Penny/MainService/PennyService.swift b/Sources/Penny/MainService/PennyService.swift index 717b2ecd..f41cb455 100644 --- a/Sources/Penny/MainService/PennyService.swift +++ b/Sources/Penny/MainService/PennyService.swift @@ -25,7 +25,7 @@ struct PennyService: MainService { message: "I'm Alive! :)", initialNoticeMention: .user(Constants.botDevUserId) ), - sendFullLogAsAttachment: .enabled, + sendFullLogsAsAttachment: .enabled, mentions: [ .warning: .user(Constants.botDevUserId), .error: .user(Constants.botDevUserId), diff --git a/Sources/Penny/Penny.swift b/Sources/Penny/Penny.swift index 3f1d0843..a8c520c1 100644 --- a/Sources/Penny/Penny.swift +++ b/Sources/Penny/Penny.swift @@ -1,5 +1,6 @@ import NIOPosix import AsyncHTTPClient +import Shared import SotoS3 @main @@ -13,13 +14,9 @@ struct Penny { /// This is preferred for apps that primarily use structured concurrency. let httpClient = HTTPClient( eventLoopGroup: MultiThreadedEventLoopGroup.singleton, - configuration: .init( - decompression: .enabled( - limit: .size(1 << 30) /// 1 GiB - ) - ) + configuration: .forPenny ) - let awsClient = AWSClient(httpClientProvider: .shared(httpClient)) + let awsClient = AWSClient(httpClient: httpClient) /// These shutdown calls are only useful for tests where we call `Penny.main()` repeatedly defer { diff --git a/Sources/Shared/+HTTPClient.swift b/Sources/Shared/+HTTPClient.swift new file mode 100644 index 00000000..216f1406 --- /dev/null +++ b/Sources/Shared/+HTTPClient.swift @@ -0,0 +1,17 @@ +import AsyncHTTPClient + +extension HTTPClient.Configuration { + package static var forPenny: Self { + HTTPClient.Configuration( + redirectConfiguration: .follow(max: 5, allowCycles: true), + timeout: .init( + connect: .seconds(15), + read: .seconds(60), + write: .seconds(60) + ), + decompression: .enabled( + limit: .ratio(100) + ) + ) + } +} diff --git a/Tests/PennyTests/Fake/FakeMainService.swift b/Tests/PennyTests/Fake/FakeMainService.swift index 2aa7fd4c..f28058ef 100644 --- a/Tests/PennyTests/Fake/FakeMainService.swift +++ b/Tests/PennyTests/Fake/FakeMainService.swift @@ -28,7 +28,8 @@ actor FakeMainService: MainService { storage: cacheStorage ) self.httpClient = HTTPClient( - eventLoopGroup: MultiThreadedEventLoopGroup.singleton + eventLoopGroup: MultiThreadedEventLoopGroup.singleton, + configuration: .forPenny ) self.context = try Self.makeContext( manager: manager, diff --git a/Tests/PennyTests/Tests/GHHooksTests.swift b/Tests/PennyTests/Tests/GHHooksTests.swift index beab2283..313b0398 100644 --- a/Tests/PennyTests/Tests/GHHooksTests.swift +++ b/Tests/PennyTests/Tests/GHHooksTests.swift @@ -14,7 +14,8 @@ import XCTest class GHHooksTests: XCTestCase { let httpClient = HTTPClient( - eventLoopGroup: MultiThreadedEventLoopGroup.singleton + eventLoopGroup: MultiThreadedEventLoopGroup.singleton, + configuration: .forPenny ) let decoder: JSONDecoder = { diff --git a/Tests/PennyTests/Tests/LeafRenderTests.swift b/Tests/PennyTests/Tests/LeafRenderTests.swift index 90f1f216..c7414cb6 100644 --- a/Tests/PennyTests/Tests/LeafRenderTests.swift +++ b/Tests/PennyTests/Tests/LeafRenderTests.swift @@ -11,7 +11,8 @@ import XCTest class LeafRenderTests: XCTestCase { let httpClient = HTTPClient( - eventLoopGroup: MultiThreadedEventLoopGroup.singleton + eventLoopGroup: MultiThreadedEventLoopGroup.singleton, + configuration: .forPenny ) lazy var ghHooksRenderClient = RenderClient( From 99047f21a12aae723851ad8ad06b037acfbab015 Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Tue, 21 May 2024 22:59:54 +0330 Subject: [PATCH 4/5] remove syncShutdown() s --- Sources/Penny/Penny.swift | 12 +++++------- Tests/PennyTests/Fake/FakeMainService.swift | 4 ---- Tests/PennyTests/Tests/GHHooksTests.swift | 4 ---- Tests/PennyTests/Tests/LeafRenderTests.swift | 4 ++-- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Sources/Penny/Penny.swift b/Sources/Penny/Penny.swift index a8c520c1..6f0269a9 100644 --- a/Sources/Penny/Penny.swift +++ b/Sources/Penny/Penny.swift @@ -18,13 +18,6 @@ struct Penny { ) let awsClient = AWSClient(httpClient: httpClient) - /// These shutdown calls are only useful for tests where we call `Penny.main()` repeatedly - defer { - /// Shutdown in reverse order of dependance. - try! awsClient.syncShutdown() - try! httpClient.syncShutdown() - } - try await mainService.bootstrapLoggingSystem(httpClient: httpClient) let bot = try await mainService.makeBot( @@ -46,5 +39,10 @@ struct Penny { for await event in await bot.events { EventHandler(event: event, context: context).handle() } + + /// These shutdown calls are only useful for tests where we call `Penny.main()` repeatedly + /// Shutdown in reverse order of dependance. + try! await awsClient.shutdown() + try! await httpClient.shutdown() } } diff --git a/Tests/PennyTests/Fake/FakeMainService.swift b/Tests/PennyTests/Fake/FakeMainService.swift index f28058ef..ae0c6024 100644 --- a/Tests/PennyTests/Fake/FakeMainService.swift +++ b/Tests/PennyTests/Fake/FakeMainService.swift @@ -38,10 +38,6 @@ actor FakeMainService: MainService { ) } - deinit { - try! httpClient.syncShutdown() - } - func bootstrapLoggingSystem(httpClient: HTTPClient) async throws { } func makeBot(httpClient: HTTPClient) async throws -> any GatewayManager { diff --git a/Tests/PennyTests/Tests/GHHooksTests.swift b/Tests/PennyTests/Tests/GHHooksTests.swift index 313b0398..74ed32dc 100644 --- a/Tests/PennyTests/Tests/GHHooksTests.swift +++ b/Tests/PennyTests/Tests/GHHooksTests.swift @@ -31,10 +31,6 @@ class GHHooksTests: XCTestCase { FakeResponseStorage.shared = FakeResponseStorage() } - override func tearDown() { - try! httpClient.syncShutdown() - } - func testUnicodesPrefix() throws { do { let scalars_16 = "Hello, world! 👍🏾" diff --git a/Tests/PennyTests/Tests/LeafRenderTests.swift b/Tests/PennyTests/Tests/LeafRenderTests.swift index c7414cb6..ae6280eb 100644 --- a/Tests/PennyTests/Tests/LeafRenderTests.swift +++ b/Tests/PennyTests/Tests/LeafRenderTests.swift @@ -34,8 +34,8 @@ class LeafRenderTests: XCTestCase { FakeResponseStorage.shared = FakeResponseStorage() } - override func tearDown() { - try! httpClient.syncShutdown() + override func tearDown() async throws { + try! await httpClient.shutdown() } func testTranslationNeededDescription() async throws { From 58903187d5f0dfbdf9a16a9f366c5fbd58cfbaa7 Mon Sep 17 00:00:00 2001 From: MahdiBM Date: Fri, 24 May 2024 14:39:06 +0330 Subject: [PATCH 5/5] no need to try! --- Sources/Penny/Penny.swift | 4 ++-- Tests/PennyTests/Tests/LeafRenderTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Penny/Penny.swift b/Sources/Penny/Penny.swift index 6f0269a9..49d9c300 100644 --- a/Sources/Penny/Penny.swift +++ b/Sources/Penny/Penny.swift @@ -42,7 +42,7 @@ struct Penny { /// These shutdown calls are only useful for tests where we call `Penny.main()` repeatedly /// Shutdown in reverse order of dependance. - try! await awsClient.shutdown() - try! await httpClient.shutdown() + try await awsClient.shutdown() + try await httpClient.shutdown() } } diff --git a/Tests/PennyTests/Tests/LeafRenderTests.swift b/Tests/PennyTests/Tests/LeafRenderTests.swift index ae6280eb..6a5af3f5 100644 --- a/Tests/PennyTests/Tests/LeafRenderTests.swift +++ b/Tests/PennyTests/Tests/LeafRenderTests.swift @@ -35,7 +35,7 @@ class LeafRenderTests: XCTestCase { } override func tearDown() async throws { - try! await httpClient.shutdown() + try await httpClient.shutdown() } func testTranslationNeededDescription() async throws {