Skip to content

Commit

Permalink
Add InstantiableRequestContext
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-fowler committed Jun 14, 2024
1 parent b4420a5 commit accb57a
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 10 deletions.
10 changes: 6 additions & 4 deletions Sources/Hummingbird/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public enum EventLoopGroupProvider {
}
}

public protocol ApplicationProtocol: Service where Context: RequestContext, Context.Source == ServerRequestContextSource {
/// Protocol for an Application. Brings together all the components of Hummingbird together
public protocol ApplicationProtocol: Service where Context: InstantiableRequestContext, Context.Source == ServerRequestContextSource {
/// Responder that generates a response from a requests and context
associatedtype Responder: HTTPResponder
/// Context passed with Request to responder
Expand Down Expand Up @@ -100,10 +101,11 @@ extension ApplicationProtocol {
eventLoopGroup: self.eventLoopGroup,
logger: self.logger
) { request, channel in
let logger = self.logger.with(metadataKey: "hb_id", value: .stringConvertible(RequestID()))
let context = Self.Responder.Context(
source: .init(
channel: channel,
logger: self.logger.with(metadataKey: "hb_id", value: .stringConvertible(RequestID()))
logger: logger
)
)
// respond to request
Expand All @@ -117,7 +119,7 @@ extension ApplicationProtocol {
response = httpError.response(allocator: channel.allocator)
default:
// this error has not been recognised
context.logger.debug("Unrecognised Error", metadata: ["error": "\(error)"])
logger.debug("Unrecognised Error", metadata: ["error": "\(error)"])
response = Response(
status: .internalServerError,
body: .init()
Expand Down Expand Up @@ -171,7 +173,7 @@ extension ApplicationProtocol {
/// try await app.runService()
/// ```
/// Editing the application setup after calling `runService` will produce undefined behaviour.
public struct Application<Responder: HTTPResponder>: ApplicationProtocol where Responder.Context: RequestContext, Responder.Context.Source == ServerRequestContextSource {
public struct Application<Responder: HTTPResponder>: ApplicationProtocol where Responder.Context: InstantiableRequestContext, Responder.Context.Source == ServerRequestContextSource {
// MARK: Member variables

/// event loop group used by application
Expand Down
11 changes: 8 additions & 3 deletions Sources/Hummingbird/Server/RequestContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,20 @@ public struct CoreRequestContext: Sendable {
}
}

/// A RequestContext that can be built from some source
public protocol InstantiableRequestContext: Sendable {
associatedtype Source
/// Initialise RequestContext from source
init(source: Source)
}

/// Protocol that all request contexts should conform to. Holds data associated with
/// a request. Provides context for request processing
public protocol RequestContext: Sendable {
public protocol RequestContext: InstantiableRequestContext {
associatedtype Source: RequestContextSource = ServerRequestContextSource
associatedtype Decoder: RequestDecoder = JSONDecoder
associatedtype Encoder: ResponseEncoder = JSONEncoder

/// Initialise RequestContext from source
init(source: Source)
/// Core context
var coreContext: CoreRequestContext { get set }
/// Maximum upload size allowed for routes that don't stream the request payload. This
Expand Down
2 changes: 1 addition & 1 deletion Sources/HummingbirdTesting/Application+Test.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public struct TestingSetup {
}

/// Extends `ApplicationProtocol` to support testing of applications
extension ApplicationProtocol where Responder.Context: RequestContext {
extension ApplicationProtocol {
// MARK: Initialization

/// Test `Application`
Expand Down
4 changes: 2 additions & 2 deletions Sources/HummingbirdTesting/RouterTestFramework.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import NIOPosix
import ServiceLifecycle

/// Test sending requests directly to router. This does not setup a live server
struct RouterTestFramework<Responder: HTTPResponder>: ApplicationTestFramework where Responder.Context: RequestContext {
struct RouterTestFramework<Responder: HTTPResponder>: ApplicationTestFramework where Responder.Context: InstantiableRequestContext {
let responder: Responder
let makeContext: @Sendable (Logger) -> Responder.Context
let services: [any Service]
let logger: Logger
let processesRunBeforeServerStart: [@Sendable () async throws -> Void]

init<App: ApplicationProtocol>(app: App) async throws where App.Responder == Responder, Responder.Context: RequestContext {
init<App: ApplicationProtocol>(app: App) async throws where App.Responder == Responder, Responder.Context: InstantiableRequestContext {
self.responder = try await app.responder
self.processesRunBeforeServerStart = app.processesRunBeforeServerStart
self.services = app.services
Expand Down
19 changes: 19 additions & 0 deletions Tests/HummingbirdTests/ApplicationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,25 @@ final class ApplicationTests: XCTestCase {
}
}

/// test we can create an application that accepts a responder with an empty context
func testEmptyRequestContext() async throws {
struct EmptyRequestContext: InstantiableRequestContext {
typealias Source = ServerRequestContextSource

init(source: Source) {}
}
let app = Application(
responder: CallbackResponder { (_: Request, _: EmptyRequestContext) in
return Response(status: .ok)
}
)
try await app.test(.live) { client in
try await client.execute(uri: "/hello", method: .get) { response in
XCTAssertEqual(response.status, .ok)
}
}
}

func testHummingbirdServices() async throws {
struct MyService: Service {
static let started = ManagedAtomic(false)
Expand Down

0 comments on commit accb57a

Please sign in to comment.