From 3c7215f307e729060dbbfda93b69c6cc07a0241e Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Sun, 10 Nov 2024 22:28:40 +0000 Subject: [PATCH 01/19] Add error handling middleware --- .../Interface/ErrorHandlingMiddleware.swift | 86 +++++++++++ .../Test_ErrorHandlingMiddleware.swift | 144 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift create mode 100644 Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift new file mode 100644 index 00000000..652e5889 --- /dev/null +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import HTTPTypes + +/// Error Handling middleware that converts an error to a HTTP response +/// +/// This is an opt-in middleware that adopters may choose to include to convert application errors +/// to a HTTP Response +/// +/// Inclusion of this ErrorHandling Middleware should be accompanied by confirming errors to +/// HTTPResponseConvertible protocol. Only errors confirming to HTTPResponseConvertible are converted to a HTTP response. Other errors are re-thrown from this middleware. +/// +/// Example usage +/// 1. Create an error type that conforms to HTTPResponseConvertible protocol +/// ```swift +/// extension MyAppError: HTTPResponseConvertible { +/// var httpStatus: HTTPResponse.Status { +/// switch self { +/// case .invalidInputFormat: +/// .badRequest +/// case .authorizationError: +/// .forbidden +/// } +/// } +/// } +/// ``` +/// +/// 2. Opt in to the error middleware while registering the handler +/// +/// ```swift +/// let handler = try await RequestHandler() +/// try handler.registerHandlers(on: transport, middlewares: [ErrorHandlingMiddleware()]) +/// + +public struct ErrorHandlingMiddleware: ServerMiddleware { + public func intercept(_ request: HTTPTypes.HTTPRequest, + body: OpenAPIRuntime.HTTPBody?, + metadata: OpenAPIRuntime.ServerRequestMetadata, + operationID: String, + next: @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?)) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { + do { + return try await next(request, body, metadata) + } catch let error as ServerError { + if let appError = error.underlyingError as? (any HTTPResponseConvertible) { + return (HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), + appError.httpBody) + } else { + throw error + } + } + } +} + +/// Protocol used by ErrorHandling middleware to map an error to a HTTPResponse +/// +/// Adopters who wish to convert their application error to a HTTPResponse +/// should confirm their error(s) to this protocol + +public protocol HTTPResponseConvertible { + /// HTTP status to return in the response + var httpStatus: HTTPResponse.Status { get } + + /// (Optional) Headers to return in the response + var httpHeaderFields: HTTPTypes.HTTPFields { get } + + /// (Optional) The body of the response to return + var httpBody: OpenAPIRuntime.HTTPBody? { get } +} + +/// Extension to HTTPResponseConvertible to provide default values for certian fields +public extension HTTPResponseConvertible { + var httpHeaderFields: HTTPTypes.HTTPFields { [:] } + var httpBody: OpenAPIRuntime.HTTPBody? { nil } +} diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift new file mode 100644 index 00000000..c278669b --- /dev/null +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftOpenAPIGenerator open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import HTTPTypes + +import XCTest +@_spi(Generated) @testable import OpenAPIRuntime + + +final class Test_ErrorHandlingMiddlewareTests: XCTestCase { + static let mockRequest: HTTPRequest = .init(soar_path: "http://abc.com", method: .get) + static let mockBody: HTTPBody = HTTPBody("hello") + static let errorHandlingMiddleware = ErrorHandlingMiddleware() + + func testSuccessfulRequest() async throws { + let response = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), operationID: "testop", + next: getNextMiddleware(failurePhase: .never)) + XCTAssertEqual(response.0.status, .ok) + } + + func testError_confirmingToProtocol_convertedToResponse() async throws { + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), operationID: "testop", + next: getNextMiddleware(failurePhase: .convertibleError)) + XCTAssertEqual(response.status, .badGateway) + XCTAssertEqual(response.headerFields, [.contentType: "application/json"]) + XCTAssertEqual(responseBody, TEST_HTTP_BODY) + } + + + func testError_confirmingToProtocolWithoutAllValues_convertedToResponse() async throws { + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), operationID: "testop", + next: getNextMiddleware(failurePhase: .partialConvertibleError)) + XCTAssertEqual(response.status, .badRequest) + XCTAssertEqual(response.headerFields, [:]) + XCTAssertEqual(responseBody, nil) + } + + func testError_notConfirmingToProtocol_throws() async throws { + + do { + _ = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), operationID: "testop", + next: getNextMiddleware(failurePhase: .nonConvertibleError)) + XCTFail("Expected error to be thrown") + } catch { + let error = error as? ServerError + XCTAssertTrue(error?.underlyingError is NonConvertibleError) + } + } + + private func getNextMiddleware(failurePhase: MockErrorMiddleware_Next.FailurePhase) -> @Sendable (HTTPTypes.HTTPRequest, + OpenAPIRuntime.HTTPBody?, + OpenAPIRuntime.ServerRequestMetadata) async throws -> + (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { + + let mockNext: @Sendable (HTTPTypes.HTTPRequest, + OpenAPIRuntime.HTTPBody?, + OpenAPIRuntime.ServerRequestMetadata) async throws -> + (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) = + { request, body, metadata in + try await MockErrorMiddleware_Next(failurePhase: failurePhase).intercept(request, + body: body, + metadata: metadata, + operationID: "testop", + next: { _,_,_ in (HTTPResponse.init(status: .ok), nil) }) + } + return mockNext + } +} + +struct MockErrorMiddleware_Next: ServerMiddleware { + enum FailurePhase { + case never + case convertibleError + case nonConvertibleError + case partialConvertibleError + } + var failurePhase: FailurePhase = .never + + @Sendable + func intercept( + _ request: HTTPRequest, + body: HTTPBody?, + metadata: ServerRequestMetadata, + operationID: String, + next: (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (HTTPResponse, HTTPBody?) + ) async throws -> (HTTPResponse, HTTPBody?) { + var error: (any Error)? + switch failurePhase { + case .never: + break + case .convertibleError: + error = ConvertibleError() + case .nonConvertibleError: + error = NonConvertibleError() + case .partialConvertibleError: + error = PartialConvertibleError() + } + + if let underlyingError = error { + throw ServerError(operationID: operationID, + request: request, + requestBody: body, + requestMetadata: metadata, + causeDescription: "", + underlyingError: underlyingError) + } + + let (response, responseBody) = try await next(request, body, metadata) + return (response, responseBody) + } +} + +struct ConvertibleError: Error, HTTPResponseConvertible { + var httpStatus: HTTPTypes.HTTPResponse.Status = HTTPResponse.Status.badGateway + var httpHeaderFields: HTTPFields = [.contentType: "application/json"] + var httpBody: OpenAPIRuntime.HTTPBody? = TEST_HTTP_BODY +} + +struct PartialConvertibleError: Error, HTTPResponseConvertible { + var httpStatus: HTTPTypes.HTTPResponse.Status = HTTPResponse.Status.badRequest +} + +struct NonConvertibleError: Error {} + +let TEST_HTTP_BODY = HTTPBody(try! JSONEncoder().encode(["error"," test error"])) From cae119014de29848b8b742589e192718ccc10d3e Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Mon, 11 Nov 2024 10:22:27 +0000 Subject: [PATCH 02/19] Convert undocumented errors to 500 --- .../Interface/ErrorHandlingMiddleware.swift | 39 +++++++++---------- .../Test_ErrorHandlingMiddleware.swift | 24 +++++------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 652e5889..f95fc968 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftOpenAPIGenerator open source project // -// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Copyright (c) 2024 Apple Inc. and the SwiftOpenAPIGenerator project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -14,16 +14,14 @@ import HTTPTypes -/// Error Handling middleware that converts an error to a HTTP response +/// An opt-in error Handling middleware that converts an error to a HTTP response. /// -/// This is an opt-in middleware that adopters may choose to include to convert application errors -/// to a HTTP Response -/// -/// Inclusion of this ErrorHandling Middleware should be accompanied by confirming errors to -/// HTTPResponseConvertible protocol. Only errors confirming to HTTPResponseConvertible are converted to a HTTP response. Other errors are re-thrown from this middleware. +/// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by confirming errors to ``HTTPResponseConvertible`` protocol. +/// Undocumented errors, i.e, errors not confriming to ``HTTPResponseConvertible`` are converted to a response with 500 status code. /// /// Example usage -/// 1. Create an error type that conforms to HTTPResponseConvertible protocol +/// 1. Create an error type that conforms to HTTPResponseConvertible protocol: +/// /// ```swift /// extension MyAppError: HTTPResponseConvertible { /// var httpStatus: HTTPResponse.Status { @@ -37,13 +35,15 @@ import HTTPTypes /// } /// ``` /// -/// 2. Opt in to the error middleware while registering the handler +/// 2. Opt in to the ``ErrorHandlingMiddleware`` while registering the handler: /// /// ```swift /// let handler = try await RequestHandler() /// try handler.registerHandlers(on: transport, middlewares: [ErrorHandlingMiddleware()]) -/// - +/// ``` +/// - Note: The placement of ``ErrorHandlingMiddleware`` in the middleware chain is important. +/// It should be determined based on the specific needs of each application. +/// Consider the order of execution and dependencies between middlewares. public struct ErrorHandlingMiddleware: ServerMiddleware { public func intercept(_ request: HTTPTypes.HTTPRequest, body: OpenAPIRuntime.HTTPBody?, @@ -57,29 +57,28 @@ public struct ErrorHandlingMiddleware: ServerMiddleware { return (HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), appError.httpBody) } else { - throw error + return (HTTPResponse(status: .internalServerError), nil) } } } } -/// Protocol used by ErrorHandling middleware to map an error to a HTTPResponse -/// -/// Adopters who wish to convert their application error to a HTTPResponse -/// should confirm their error(s) to this protocol - +/// Protocol used by ErrorHandling middleware to map an error to a HTTPResponse. +/// Adopters who wish to convert their application error to a HTTPResponse should confirm their error(s) to this protocol. public protocol HTTPResponseConvertible { - /// HTTP status to return in the response + + /// HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } - /// (Optional) Headers to return in the response + /// Headers to return in the response. + /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } /// (Optional) The body of the response to return var httpBody: OpenAPIRuntime.HTTPBody? { get } } -/// Extension to HTTPResponseConvertible to provide default values for certian fields +/// Extension to HTTPResponseConvertible to provide default values for certian fields. public extension HTTPResponseConvertible { var httpHeaderFields: HTTPTypes.HTTPFields { [:] } var httpBody: OpenAPIRuntime.HTTPBody? { nil } diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift index c278669b..ac7f29ca 100644 --- a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftOpenAPIGenerator open source project // -// Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors +// Copyright (c) 2024 Apple Inc. and the SwiftOpenAPIGenerator project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -46,24 +46,20 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, body: Test_ErrorHandlingMiddlewareTests.mockBody, metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .partialConvertibleError)) + next: getNextMiddleware(failurePhase: .partialConvertibleError)) XCTAssertEqual(response.status, .badRequest) XCTAssertEqual(response.headerFields, [:]) XCTAssertEqual(responseBody, nil) } - func testError_notConfirmingToProtocol_throws() async throws { - - do { - _ = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, - body: Test_ErrorHandlingMiddlewareTests.mockBody, - metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .nonConvertibleError)) - XCTFail("Expected error to be thrown") - } catch { - let error = error as? ServerError - XCTAssertTrue(error?.underlyingError is NonConvertibleError) - } + func testError_notConfirmingToProtocol_returns500() async throws { + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), operationID: "testop", + next: getNextMiddleware(failurePhase: .nonConvertibleError)) + XCTAssertEqual(response.status, .internalServerError) + XCTAssertEqual(response.headerFields, [:]) + XCTAssertEqual(responseBody, nil) } private func getNextMiddleware(failurePhase: MockErrorMiddleware_Next.FailurePhase) -> @Sendable (HTTPTypes.HTTPRequest, From b83b99266c2e8133d7beb8847d279731f03eb925 Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Mon, 11 Nov 2024 11:48:39 +0000 Subject: [PATCH 03/19] CI checks --- .../Interface/ErrorHandlingMiddleware.swift | 25 ++-- .../Test_ErrorHandlingMiddleware.swift | 113 +++++++++--------- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index f95fc968..2de8faaa 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -45,17 +45,20 @@ import HTTPTypes /// It should be determined based on the specific needs of each application. /// Consider the order of execution and dependencies between middlewares. public struct ErrorHandlingMiddleware: ServerMiddleware { - public func intercept(_ request: HTTPTypes.HTTPRequest, - body: OpenAPIRuntime.HTTPBody?, - metadata: OpenAPIRuntime.ServerRequestMetadata, - operationID: String, - next: @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?)) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { - do { - return try await next(request, body, metadata) - } catch let error as ServerError { + public func intercept( + _ request: HTTPTypes.HTTPRequest, + body: OpenAPIRuntime.HTTPBody?, + metadata: OpenAPIRuntime.ServerRequestMetadata, + operationID: String, + next: @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata) + async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) + ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { + do { return try await next(request, body, metadata) } catch let error as ServerError { if let appError = error.underlyingError as? (any HTTPResponseConvertible) { - return (HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), - appError.httpBody) + return ( + HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), + appError.httpBody + ) } else { return (HTTPResponse(status: .internalServerError), nil) } @@ -69,11 +72,9 @@ public protocol HTTPResponseConvertible { /// HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } - /// Headers to return in the response. /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } - /// (Optional) The body of the response to return var httpBody: OpenAPIRuntime.HTTPBody? { get } } diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift index ac7f29ca..7832e552 100644 --- a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -17,67 +17,73 @@ import HTTPTypes import XCTest @_spi(Generated) @testable import OpenAPIRuntime - final class Test_ErrorHandlingMiddlewareTests: XCTestCase { static let mockRequest: HTTPRequest = .init(soar_path: "http://abc.com", method: .get) static let mockBody: HTTPBody = HTTPBody("hello") static let errorHandlingMiddleware = ErrorHandlingMiddleware() func testSuccessfulRequest() async throws { - let response = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, - body: Test_ErrorHandlingMiddlewareTests.mockBody, - metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .never)) + let response = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( + Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), + operationID: "testop", + next: getNextMiddleware(failurePhase: .never) + ) XCTAssertEqual(response.0.status, .ok) } - func testError_confirmingToProtocol_convertedToResponse() async throws { - let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, - body: Test_ErrorHandlingMiddlewareTests.mockBody, - metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .convertibleError)) + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( + Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), + operationID: "testop", + next: getNextMiddleware(failurePhase: .convertibleError) + ) XCTAssertEqual(response.status, .badGateway) XCTAssertEqual(response.headerFields, [.contentType: "application/json"]) XCTAssertEqual(responseBody, TEST_HTTP_BODY) } - func testError_confirmingToProtocolWithoutAllValues_convertedToResponse() async throws { - let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, - body: Test_ErrorHandlingMiddlewareTests.mockBody, - metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .partialConvertibleError)) + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( + Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), + operationID: "testop", + next: getNextMiddleware(failurePhase: .partialConvertibleError) + ) XCTAssertEqual(response.status, .badRequest) XCTAssertEqual(response.headerFields, [:]) XCTAssertEqual(responseBody, nil) } - func testError_notConfirmingToProtocol_returns500() async throws { - let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept(Test_ErrorHandlingMiddlewareTests.mockRequest, - body: Test_ErrorHandlingMiddlewareTests.mockBody, - metadata: .init(), operationID: "testop", - next: getNextMiddleware(failurePhase: .nonConvertibleError)) + let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( + Test_ErrorHandlingMiddlewareTests.mockRequest, + body: Test_ErrorHandlingMiddlewareTests.mockBody, + metadata: .init(), + operationID: "testop", + next: getNextMiddleware(failurePhase: .nonConvertibleError) + ) XCTAssertEqual(response.status, .internalServerError) XCTAssertEqual(response.headerFields, [:]) XCTAssertEqual(responseBody, nil) } - - private func getNextMiddleware(failurePhase: MockErrorMiddleware_Next.FailurePhase) -> @Sendable (HTTPTypes.HTTPRequest, - OpenAPIRuntime.HTTPBody?, - OpenAPIRuntime.ServerRequestMetadata) async throws -> - (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { - - let mockNext: @Sendable (HTTPTypes.HTTPRequest, - OpenAPIRuntime.HTTPBody?, - OpenAPIRuntime.ServerRequestMetadata) async throws -> - (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) = - { request, body, metadata in - try await MockErrorMiddleware_Next(failurePhase: failurePhase).intercept(request, - body: body, - metadata: metadata, - operationID: "testop", - next: { _,_,_ in (HTTPResponse.init(status: .ok), nil) }) - } + private func getNextMiddleware(failurePhase: MockErrorMiddleware_Next.FailurePhase) -> @Sendable ( + HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata + ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { + let mockNext: + @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata) + async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) = { request, body, metadata in + try await MockErrorMiddleware_Next(failurePhase: failurePhase) + .intercept( + request, + body: body, + metadata: metadata, + operationID: "testop", + next: { _, _, _ in (HTTPResponse.init(status: .ok), nil) } + ) + } return mockNext } } @@ -91,8 +97,7 @@ struct MockErrorMiddleware_Next: ServerMiddleware { } var failurePhase: FailurePhase = .never - @Sendable - func intercept( + @Sendable func intercept( _ request: HTTPRequest, body: HTTPBody?, metadata: ServerRequestMetadata, @@ -101,25 +106,21 @@ struct MockErrorMiddleware_Next: ServerMiddleware { ) async throws -> (HTTPResponse, HTTPBody?) { var error: (any Error)? switch failurePhase { - case .never: - break - case .convertibleError: - error = ConvertibleError() - case .nonConvertibleError: - error = NonConvertibleError() - case .partialConvertibleError: - error = PartialConvertibleError() + case .never: break + case .convertibleError: error = ConvertibleError() + case .nonConvertibleError: error = NonConvertibleError() + case .partialConvertibleError: error = PartialConvertibleError() } - if let underlyingError = error { - throw ServerError(operationID: operationID, - request: request, - requestBody: body, - requestMetadata: metadata, - causeDescription: "", - underlyingError: underlyingError) + throw ServerError( + operationID: operationID, + request: request, + requestBody: body, + requestMetadata: metadata, + causeDescription: "", + underlyingError: underlyingError + ) } - let (response, responseBody) = try await next(request, body, metadata) return (response, responseBody) } @@ -137,4 +138,4 @@ struct PartialConvertibleError: Error, HTTPResponseConvertible { struct NonConvertibleError: Error {} -let TEST_HTTP_BODY = HTTPBody(try! JSONEncoder().encode(["error"," test error"])) +let TEST_HTTP_BODY = HTTPBody(try! JSONEncoder().encode(["error", " test error"])) From 7201b6121a95969e778c3957026cd8be2f928aad Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:15:47 +0000 Subject: [PATCH 04/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 2de8faaa..ad0bc476 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -14,7 +14,7 @@ import HTTPTypes -/// An opt-in error Handling middleware that converts an error to a HTTP response. +/// An opt-in error handling middleware that converts an error to a HTTP response. /// /// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by confirming errors to ``HTTPResponseConvertible`` protocol. /// Undocumented errors, i.e, errors not confriming to ``HTTPResponseConvertible`` are converted to a response with 500 status code. From fe5e4dcd616e42ddb9caa8561a4434d03e18c500 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:15:59 +0000 Subject: [PATCH 05/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index ad0bc476..909e206a 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -16,7 +16,7 @@ import HTTPTypes /// An opt-in error handling middleware that converts an error to a HTTP response. /// -/// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by confirming errors to ``HTTPResponseConvertible`` protocol. +/// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by conforming errors to the ``HTTPResponseConvertible`` protocol. /// Undocumented errors, i.e, errors not confriming to ``HTTPResponseConvertible`` are converted to a response with 500 status code. /// /// Example usage From c55adcfc9740bc15746cdfb2e0132d85d69dc916 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:16:13 +0000 Subject: [PATCH 06/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 909e206a..3a9a28f9 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -17,7 +17,7 @@ import HTTPTypes /// An opt-in error handling middleware that converts an error to a HTTP response. /// /// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by conforming errors to the ``HTTPResponseConvertible`` protocol. -/// Undocumented errors, i.e, errors not confriming to ``HTTPResponseConvertible`` are converted to a response with 500 status code. +/// Errors not conforming to ``HTTPResponseConvertible`` are converted to a response with the 500 status code. /// /// Example usage /// 1. Create an error type that conforms to HTTPResponseConvertible protocol: From 91a282e81ddafa8dcedcd04b3202d2e543b8af60 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:16:25 +0000 Subject: [PATCH 07/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 3a9a28f9..6eb1eb85 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -19,7 +19,7 @@ import HTTPTypes /// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by conforming errors to the ``HTTPResponseConvertible`` protocol. /// Errors not conforming to ``HTTPResponseConvertible`` are converted to a response with the 500 status code. /// -/// Example usage +/// ## Example usage /// 1. Create an error type that conforms to HTTPResponseConvertible protocol: /// /// ```swift From 31698187e61e9066c6a461fd3b7462580078372d Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:16:37 +0000 Subject: [PATCH 08/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 6eb1eb85..99992458 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -20,7 +20,7 @@ import HTTPTypes /// Errors not conforming to ``HTTPResponseConvertible`` are converted to a response with the 500 status code. /// /// ## Example usage -/// 1. Create an error type that conforms to HTTPResponseConvertible protocol: +/// 1. Create an error type that conforms to the ``HTTPResponseConvertible`` protocol: /// /// ```swift /// extension MyAppError: HTTPResponseConvertible { From a683e894ddfe54d00100a4570406f775466fd250 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:17:05 +0000 Subject: [PATCH 09/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 99992458..1011d75e 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -73,6 +73,7 @@ public protocol HTTPResponseConvertible { /// HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } /// Headers to return in the response. + /// /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } /// (Optional) The body of the response to return From d9fa8989c5d1f5ee08414b997fe604888dfa4ca8 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:17:13 +0000 Subject: [PATCH 10/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 1011d75e..6557cc8c 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -76,7 +76,7 @@ public protocol HTTPResponseConvertible { /// /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } - /// (Optional) The body of the response to return + /// The body of the HTTP response. var httpBody: OpenAPIRuntime.HTTPBody? { get } } From 2fb41238f182dd263c650f4acf12aa2626e2772a Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:17:25 +0000 Subject: [PATCH 11/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 6557cc8c..4790fddf 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -72,7 +72,7 @@ public protocol HTTPResponseConvertible { /// HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } - /// Headers to return in the response. + /// The HTTP headers of the response. /// /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } From e4e73595f7940bc23fd0e853dabdeb28b9fdec5d Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:17:37 +0000 Subject: [PATCH 12/19] Update Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- .../Interface/Test_ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift index 7832e552..16e60f5e 100644 --- a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -138,4 +138,4 @@ struct PartialConvertibleError: Error, HTTPResponseConvertible { struct NonConvertibleError: Error {} -let TEST_HTTP_BODY = HTTPBody(try! JSONEncoder().encode(["error", " test error"])) +let testHTTPBody = HTTPBody(try! JSONEncoder().encode(["error", " test error"])) From adb088a856355832d68764aef51d69af448e2070 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:18:41 +0000 Subject: [PATCH 13/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- .../Interface/ErrorHandlingMiddleware.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 4790fddf..71e497e6 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -80,8 +80,8 @@ public protocol HTTPResponseConvertible { var httpBody: OpenAPIRuntime.HTTPBody? { get } } -/// Extension to HTTPResponseConvertible to provide default values for certian fields. -public extension HTTPResponseConvertible { - var httpHeaderFields: HTTPTypes.HTTPFields { [:] } - var httpBody: OpenAPIRuntime.HTTPBody? { nil } +/// Extension to HTTPResponseConvertible to provide default values for certain fields. +extension HTTPResponseConvertible { + public var httpHeaderFields: HTTPTypes.HTTPFields { [:] } + public var httpBody: OpenAPIRuntime.HTTPBody? { nil } } From 1a89ccd0931d61034d5f2c83634cdcb1bcd1924d Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Tue, 19 Nov 2024 00:03:41 +0000 Subject: [PATCH 14/19] Apply suggestions from code review --- .../Interface/ErrorHandlingMiddleware.swift | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index 71e497e6..a77d703e 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -25,7 +25,7 @@ import HTTPTypes /// ```swift /// extension MyAppError: HTTPResponseConvertible { /// var httpStatus: HTTPResponse.Status { -/// switch self { +/// switch self { /// case .invalidInputFormat: /// .badRequest /// case .authorizationError: @@ -38,13 +38,25 @@ import HTTPTypes /// 2. Opt in to the ``ErrorHandlingMiddleware`` while registering the handler: /// /// ```swift -/// let handler = try await RequestHandler() +/// let handler = RequestHandler() /// try handler.registerHandlers(on: transport, middlewares: [ErrorHandlingMiddleware()]) /// ``` /// - Note: The placement of ``ErrorHandlingMiddleware`` in the middleware chain is important. /// It should be determined based on the specific needs of each application. /// Consider the order of execution and dependencies between middlewares. public struct ErrorHandlingMiddleware: ServerMiddleware { + + /// Creates an ErrorHandlingMiddleware. + public init() {} + + /// Intercepts an outgoing HTTP request and an incoming HTTP Response, and converts errors(if any) that confirms to ``HTTPResponseConvertible`` to an HTTP response. + /// - Parameters: + /// - request: The HTTP request created during the operation. + /// - body: The HTTP request body created during the operation. + /// - metadata: Request metadata + /// - operationID: The OpenAPI operation identifier. + /// - next: A closure that calls the next middleware, or the transport. + /// - Returns: An HTTPResponse and an optional HTTPBody. public func intercept( _ request: HTTPTypes.HTTPRequest, body: OpenAPIRuntime.HTTPBody?, @@ -66,14 +78,13 @@ public struct ErrorHandlingMiddleware: ServerMiddleware { } } -/// Protocol used by ErrorHandling middleware to map an error to a HTTPResponse. -/// Adopters who wish to convert their application error to a HTTPResponse should confirm their error(s) to this protocol. +/// A protocol used by ``ErrorHandlingMiddleware`` to map an error to an ``HTTPResponse`` and ``HTTPBody``. +/// Adopters who wish to convert their application error to an `HTTPResponse` and ``HTTPBody`` should conform the error type to this protocol. public protocol HTTPResponseConvertible { - /// HTTP status to return in the response. + /// An HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } /// The HTTP headers of the response. - /// /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } /// The body of the HTTP response. From cc2282cc465ac4bb9bac03508d2e020896c59e3d Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:32:55 +0000 Subject: [PATCH 15/19] Update Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift Co-authored-by: Honza Dvorsky --- Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index a77d703e..b3c96a1f 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -14,7 +14,7 @@ import HTTPTypes -/// An opt-in error handling middleware that converts an error to a HTTP response. +/// An opt-in error handling middleware that converts an error to an HTTP response. /// /// Inclusion of ``ErrorHandlingMiddleware`` should be accompanied by conforming errors to the ``HTTPResponseConvertible`` protocol. /// Errors not conforming to ``HTTPResponseConvertible`` are converted to a response with the 500 status code. From d1fd86dfe2c55d3a3cba42344c3027c623cb2dde Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:36:29 +0000 Subject: [PATCH 16/19] Apply suggestions from code review Co-authored-by: Honza Dvorsky --- .../Interface/ErrorHandlingMiddleware.swift | 34 +++++++++---------- .../Test_ErrorHandlingMiddleware.swift | 9 +++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index b3c96a1f..dab03198 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -20,6 +20,7 @@ import HTTPTypes /// Errors not conforming to ``HTTPResponseConvertible`` are converted to a response with the 500 status code. /// /// ## Example usage +/// /// 1. Create an error type that conforms to the ``HTTPResponseConvertible`` protocol: /// /// ```swift @@ -35,28 +36,19 @@ import HTTPTypes /// } /// ``` /// -/// 2. Opt in to the ``ErrorHandlingMiddleware`` while registering the handler: +/// 2. Opt into the ``ErrorHandlingMiddleware`` while registering the handler: /// /// ```swift /// let handler = RequestHandler() /// try handler.registerHandlers(on: transport, middlewares: [ErrorHandlingMiddleware()]) /// ``` -/// - Note: The placement of ``ErrorHandlingMiddleware`` in the middleware chain is important. -/// It should be determined based on the specific needs of each application. -/// Consider the order of execution and dependencies between middlewares. +/// - Note: The placement of ``ErrorHandlingMiddleware`` in the middleware chain is important. It should be determined based on the specific needs of each application. Consider the order of execution and dependencies between middlewares. public struct ErrorHandlingMiddleware: ServerMiddleware { - /// Creates an ErrorHandlingMiddleware. + /// Creates a new middleware. public init() {} - /// Intercepts an outgoing HTTP request and an incoming HTTP Response, and converts errors(if any) that confirms to ``HTTPResponseConvertible`` to an HTTP response. - /// - Parameters: - /// - request: The HTTP request created during the operation. - /// - body: The HTTP request body created during the operation. - /// - metadata: Request metadata - /// - operationID: The OpenAPI operation identifier. - /// - next: A closure that calls the next middleware, or the transport. - /// - Returns: An HTTPResponse and an optional HTTPBody. + // swift-format-ignore: AllPublicDeclarationsHaveDocumentation public func intercept( _ request: HTTPTypes.HTTPRequest, body: OpenAPIRuntime.HTTPBody?, @@ -78,21 +70,29 @@ public struct ErrorHandlingMiddleware: ServerMiddleware { } } -/// A protocol used by ``ErrorHandlingMiddleware`` to map an error to an ``HTTPResponse`` and ``HTTPBody``. -/// Adopters who wish to convert their application error to an `HTTPResponse` and ``HTTPBody`` should conform the error type to this protocol. +/// A value that can be converted to an HTTP response and body. +/// +/// Conform your error type to this protocol to convert it to an `HTTPResponse` and ``HTTPBody``. +/// +/// Used by ``ErrorHandlingMiddleware``. public protocol HTTPResponseConvertible { /// An HTTP status to return in the response. var httpStatus: HTTPResponse.Status { get } - /// The HTTP headers of the response. + + /// The HTTP header fields of the response. /// This is optional as default values are provided in the extension. var httpHeaderFields: HTTPTypes.HTTPFields { get } + /// The body of the HTTP response. var httpBody: OpenAPIRuntime.HTTPBody? { get } } -/// Extension to HTTPResponseConvertible to provide default values for certain fields. extension HTTPResponseConvertible { + + // swift-format-ignore: AllPublicDeclarationsHaveDocumentation public var httpHeaderFields: HTTPTypes.HTTPFields { [:] } + + // swift-format-ignore: AllPublicDeclarationsHaveDocumentation public var httpBody: OpenAPIRuntime.HTTPBody? { nil } } diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift index 16e60f5e..4d2f9504 100644 --- a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -32,7 +32,8 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { ) XCTAssertEqual(response.0.status, .ok) } - func testError_confirmingToProtocol_convertedToResponse() async throws { + + func testError_conformingToProtocol_convertedToResponse() async throws { let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( Test_ErrorHandlingMiddlewareTests.mockRequest, body: Test_ErrorHandlingMiddlewareTests.mockBody, @@ -45,7 +46,7 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { XCTAssertEqual(responseBody, TEST_HTTP_BODY) } - func testError_confirmingToProtocolWithoutAllValues_convertedToResponse() async throws { + func testError_conformingToProtocolWithoutAllValues_convertedToResponse() async throws { let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( Test_ErrorHandlingMiddlewareTests.mockRequest, body: Test_ErrorHandlingMiddlewareTests.mockBody, @@ -57,7 +58,8 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { XCTAssertEqual(response.headerFields, [:]) XCTAssertEqual(responseBody, nil) } - func testError_notConfirmingToProtocol_returns500() async throws { + + func testError_notConformingToProtocol_returns500() async throws { let (response, responseBody) = try await Test_ErrorHandlingMiddlewareTests.errorHandlingMiddleware.intercept( Test_ErrorHandlingMiddlewareTests.mockRequest, body: Test_ErrorHandlingMiddlewareTests.mockBody, @@ -69,6 +71,7 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { XCTAssertEqual(response.headerFields, [:]) XCTAssertEqual(responseBody, nil) } + private func getNextMiddleware(failurePhase: MockErrorMiddleware_Next.FailurePhase) -> @Sendable ( HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { From 1516ff6bd218cf73d3d92d7b0fbf46a2213792e1 Mon Sep 17 00:00:00 2001 From: gayathrisairam <168187165+gayathrisairam@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:37:03 +0000 Subject: [PATCH 17/19] Apply suggestions from code review Co-authored-by: Honza Dvorsky --- .../OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index dab03198..c86d7d5f 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -57,8 +57,8 @@ public struct ErrorHandlingMiddleware: ServerMiddleware { next: @Sendable (HTTPTypes.HTTPRequest, OpenAPIRuntime.HTTPBody?, OpenAPIRuntime.ServerRequestMetadata) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { - do { return try await next(request, body, metadata) } catch let error as ServerError { - if let appError = error.underlyingError as? (any HTTPResponseConvertible) { + do { return try await next(request, body, metadata) } catch { + if let serverError = error as? ServerError, let appError = serverError.underlyingError as? (any HTTPResponseConvertible) { return ( HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), appError.httpBody From a4f5df41755bcc2f251a440d1f181831c1f5dfbc Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Tue, 19 Nov 2024 17:24:19 +0000 Subject: [PATCH 18/19] Fix unit test --- .../Interface/Test_ErrorHandlingMiddleware.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift index 4d2f9504..91977a31 100644 --- a/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift +++ b/Tests/OpenAPIRuntimeTests/Interface/Test_ErrorHandlingMiddleware.swift @@ -43,7 +43,7 @@ final class Test_ErrorHandlingMiddlewareTests: XCTestCase { ) XCTAssertEqual(response.status, .badGateway) XCTAssertEqual(response.headerFields, [.contentType: "application/json"]) - XCTAssertEqual(responseBody, TEST_HTTP_BODY) + XCTAssertEqual(responseBody, testHTTPBody) } func testError_conformingToProtocolWithoutAllValues_convertedToResponse() async throws { @@ -132,7 +132,7 @@ struct MockErrorMiddleware_Next: ServerMiddleware { struct ConvertibleError: Error, HTTPResponseConvertible { var httpStatus: HTTPTypes.HTTPResponse.Status = HTTPResponse.Status.badGateway var httpHeaderFields: HTTPFields = [.contentType: "application/json"] - var httpBody: OpenAPIRuntime.HTTPBody? = TEST_HTTP_BODY + var httpBody: OpenAPIRuntime.HTTPBody? = testHTTPBody } struct PartialConvertibleError: Error, HTTPResponseConvertible { From 4e7c63876b9659b31d57c98adafbeb8baa463c5b Mon Sep 17 00:00:00 2001 From: Gayathri Sairamkrishnan Date: Wed, 20 Nov 2024 12:30:41 +0000 Subject: [PATCH 19/19] Fix formatting issues --- .../OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift index c86d7d5f..55113ce5 100644 --- a/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift +++ b/Sources/OpenAPIRuntime/Interface/ErrorHandlingMiddleware.swift @@ -44,10 +44,8 @@ import HTTPTypes /// ``` /// - Note: The placement of ``ErrorHandlingMiddleware`` in the middleware chain is important. It should be determined based on the specific needs of each application. Consider the order of execution and dependencies between middlewares. public struct ErrorHandlingMiddleware: ServerMiddleware { - /// Creates a new middleware. public init() {} - // swift-format-ignore: AllPublicDeclarationsHaveDocumentation public func intercept( _ request: HTTPTypes.HTTPRequest, @@ -58,7 +56,9 @@ public struct ErrorHandlingMiddleware: ServerMiddleware { async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) ) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?) { do { return try await next(request, body, metadata) } catch { - if let serverError = error as? ServerError, let appError = serverError.underlyingError as? (any HTTPResponseConvertible) { + if let serverError = error as? ServerError, + let appError = serverError.underlyingError as? (any HTTPResponseConvertible) + { return ( HTTPResponse(status: appError.httpStatus, headerFields: appError.httpHeaderFields), appError.httpBody