hummingbird-project / hummingbird

Lightweight, flexible HTTP server framework written in Swift
Apache License 2.0
1.16k stars 53 forks source link

Add additional result builder capabilities. #584

Closed connor-ricks closed 3 days ago

connor-ricks commented 6 days ago

Description

This PR extends the supported functionality of the MiddlewareFixedTypeBuilder result builder.

The addition of these result builders also empowers HummingbirdRouter unlocking the ability for RouteBuilder and RouterController objects to make use of this conditional syntax as well.

[!Warning] It might be necessary to add tests to validate that HummingbirdRouter can also make use of these syntax additions. Although, given that we have tests that cover the ability to use controllers in the MiddlewareFixedTypeBuilder result builder, the support may be "inherently" tested.

Conditional if-else, if and optional statements

Adding support for conditional statements in result builders further enhances the ability to perform logical operations when composing your application's routes and middleware.

if-else

Some applications may want to conditionally add different middleware based on various properties or configurations of their application. In the below example, we utilize different logging middleware if we are running in release mode.

router.addMiddleware {
    if isRelease {
        ReleaseLoggingMiddleware()
    } else {
        DebugLoggingMiddleware()
    }
    ...
}

if

Some applications may want to conditionally add middleware based on various properties or configurations of their application. In the below example, we attach a middleware that exposes the internal errors of our application to aid in client side debugging during development.

router.addMiddleware {
    if isDevelopment {
        DebugErrorDescriptionMiddleware()
    }
    ...
}

optionals

Sometimes our middleware builders may have an optional middleware that they would like to render. In the below example, we build our middleware in a function that takes an optional logger. We unwrap the middleware and add it to the router if it exists.

func buildMiddleware<M: RouterMiddleware>(logger: M?) async throws where M.Context == BasicRequestContext {
    self.router.addMiddleware {
        if let logger {
            logger
        }
        ...
    }
}

for-loop

Sometimes we may build middleware based on a provided set of data. In the below example, we add a series of tracking middleware based on the provided collection of Tracker objects.

enum Tracker: String {
    case sentry
    case datadog
    case firebase
}

func add(trackers: [Tracker]) {
    self.router.addMiddleware {
        for tracker in trackers {
            switch tracker {
            case .datadog:
                DatadogMiddleware()
            case .firebase:
                FirebaseMiddleware()
            case .sentry:
                SentryMiddleware()
            }
        }
    }
}
codecov[bot] commented 6 days ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 83.41%. Comparing base (e78cde7) to head (7f47c97). Report is 156 commits behind head on main.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #584 +/- ## ========================================== - Coverage 84.86% 83.41% -1.45% ========================================== Files 98 104 +6 Lines 5320 4451 -869 ========================================== - Hits 4515 3713 -802 + Misses 805 738 -67 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

connor-ricks commented 3 days ago

Added. 👍 Thanks for the review!