Skip to content

Commit

Permalink
Router update (#29)
Browse files Browse the repository at this point in the history
* RouterGuide update

* Update Hummingbird symbols

* Update Logging, metrics, tracing guide

* Update for symbol changes
  • Loading branch information
adam-fowler authored May 1, 2024
1 parent 7fafa61 commit c0947d2
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 40 deletions.
20 changes: 13 additions & 7 deletions Hummingbird.docc/Articles/LoggingMetricsAndTracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ Hummingbird has middleware for each of these systems. As these are provided as m

## Logging

Logs provides a record of discrete events over time. Each event has a timestamp, description and an array of metadata. Hummingbird automatically does some logging of events. You can control the fidelity of your logging by setting the application log level eg
Logs provides a record of discrete events over time. Each event has a timestamp, description and an array of metadata. Hummingbird automatically does some logging of events. You can control the fidelity of your logging by providing your own `Logger` when creating your `Application` eg

```swift
application.logger.logLevel = .debug
var logger = Logger(label: "MyLogger")
logger.logLevel = .debug
let application = Application(
router: router,
logger: logger
)
```

If you want a record of every request to the server you can add the ``LogRequestsMiddleware`` middleware. You can control at what `logLevel` the request logging will occur and whether it includes information about each requests headers. eg

```swift
application.middleware.add(LogRequestsMiddleware(.info, includeHeaders: false))
let router = Router()
router.middlewares.add(LogRequestsMiddleware(.debug, includeHeaders: false))
```

If you would like to add your own logging, or implement your own logging backend you can find out more [here](https://swiftpackageindex.com/apple/swift-log/main/documentation/logging). A complete list of logging implementations can be found [here](https://github.com/apple/swift-log#selecting-a-logging-backend-implementation-applications-only).
Expand All @@ -38,8 +44,8 @@ import Prometheus
let prometheus = PrometheusClient()
MetricsSystem.bootstrap(PrometheusMetricsFactory(client: prometheus))

// Add metrics middleware
application.middleware.add(MetricsMiddleware())
// Add metrics middleware to router
router.middlewares.add(MetricsMiddleware())
```

If you would like to record your own metrics, or implement your own metrics backed you can find out more [here](https://swiftpackageindex.com/apple/swift-metrics/main/documentation/coremetrics). A list of metrics backend implementations can be found [here](https://github.com/apple/swift-metrics#selecting-a-metrics-backend-implementation-applications-only).
Expand All @@ -55,12 +61,12 @@ import OpenTelemetry
import Tracing

// Bootstrap Open Telemetry
let otel = OTel(serviceName: "example", eventLoopGroup: application.eventLoopGroup)
let otel = OTel(serviceName: "example", eventLoopGroup: .singleton)
try otel.start().wait()
InstrumentationSystem.bootstrap(otel.tracer())

// Add tracing middleware
application.middleware.add(TracingMiddleware(recordingHeaders: ["content-type", "content-length"]))
router.middlewares.add(TracingMiddleware(recordingHeaders: ["content-type", "content-length"]))
```

If you would like to find out more about tracing, or implement your own tracing backend you can find out more [here](https://swiftpackageindex.com/apple/swift-distributed-tracing/main/documentation/tracing).
Expand Down
42 changes: 16 additions & 26 deletions Hummingbird.docc/Articles/RouterGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The router directs requests to their handlers based on the contents of their pat

## Overview

The default router that comes with Hummingbird uses a Trie based lookup. Routes are added using the function ``Hummingbird/Router/on(_:method:use:)-3copy``. You provide the URI path, the method and the handler function. Below is a simple route which returns "Hello" in the body of the response.
The default router that comes with Hummingbird uses a Trie based lookup. Routes are added using the function ``Hummingbird/Router/on(_:method:use:)``. You provide the URI path, the method and the handler function. Below is a simple route which returns "Hello" in the body of the response.

```swift
let router = Router()
Expand Down Expand Up @@ -138,41 +138,31 @@ app.router.group("/todos")
.delete("{id}", deleteTodo)
```

### Route handlers
### Route Collections

A route handler `RouteHandler` allows you to encapsulate all the components required for a route, and provide separation of the extraction of input parameters from the request and the processing of those parameters. An example could be structrured as follows
A ``RouteCollection`` is a collection of routes and middleware that can be added to a `Router` in one go. It has the same API as `RouterGroup`, so can have groups internal to the collection to allow for Middleware to applied to only sub-sections of the `RouteCollection`.

```swift
struct AddOrder: RouteHandler {
struct Input: Decodable {
let name: String
let amount: Double
}
struct Output: ResponseEncodable {
let id: String
}
let input: Input
let user: User

init(from request: Request, context: some AuthRequestContext) async throws {
self.input = try await request.decode(as: Input.self, context: context)
self.user = try context.auth.require(User.self)
}
func handle(context: some AuthRequestContext) async throws -> Output {
let order = Order(user: self.user.id, details: self.input)
let order = try await order.save(on: db)
return Output(id: order.id)
struct UserController<Context: BaseRequestContext> {
var routes: RouteCollection<Context> {
let routes = RouteCollection()
routes.post("signup", use: signUp)
routes.group("login")
.add(middleware: BasicAuthenticationMiddleware())
.post(use: login)
return routes
}
}
```
Here you can see the `AddOrder` route handler encapsulates everything you need to know about the add order route. The `Input` and `Output` structs are defined and any additional input parameters that need extracted from the `Request`. The input parameters are extracted in the `init` and then the those parameters are processed in the `handle` function. In this example we need to decode the `Input` from the `Request` and using the authentication framework from `HummingbirdAuth` we get the authenticated user.

The following will add the handler to the application
You add the route collection to your router using ``Router/addRoutes(_:atPath:)``.

```swift
application.router.put("order", use: AddOrder.self)
let router = Router()
router.add("users", routes: UserController().routes)
```

### Request body
### Request Body

By default the request body is an AsyncSequence of ByteBuffers. You can treat it as a series of buffers or collect it into one larger buffer.

Expand Down
2 changes: 1 addition & 1 deletion Hummingbird.docc/Hummingbird/Hummingbird.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ try await app.runService()

- ``Router``
- ``RouterGroup``
- ``RouteCollection``
- ``RouterMethods``
- ``RouterOptions``
- ``RouteHandler``
- ``HTTPResponder``
- ``HTTPResponderBuilder``
- ``CallbackResponder``
Expand Down
5 changes: 0 additions & 5 deletions Hummingbird.docc/HummingbirdCore/HummingbirdCore.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,6 @@ try await serviceGroup.run()
- ``HTTPError``
- ``HTTPResponseError``

### Client

- ``ClientConnection``
- ``ClientConnectionChannel``

### Miscellaneous

- ``FlatDictionary``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Before a HTTP connection can be upgraded to a WebSocket connection a server must

## Setup

You can access this by setting the `server` parameter in `Application.init()` to ``/HummingbirdCore/HTTPChannelBuilder/http1WebSocketUpgrade(configuration:additionalChannelHandlers:shouldUpgrade:)-3nbre``. This is initialized with a closure that returns either ``/HummingbirdWebSocket/ShouldUpgradeResult/dontUpgrade`` to not perform the WebSocket upgrade or ``/HummingbirdWebSocket/ShouldUpgradeResult/upgrade(_:_:)`` along with the closure handling the WebSocket connection.
You can access this by setting the `server` parameter in `Application.init()` to ``/HummingbirdCore/HTTPChannelBuilder/http1WebSocketUpgrade(configuration:additionalChannelHandlers:shouldUpgrade:)-txx5``. This is initialized with a closure that returns either ``/HummingbirdWebSocket/ShouldUpgradeResult/dontUpgrade`` to not perform the WebSocket upgrade or ``/HummingbirdWebSocket/ShouldUpgradeResult/upgrade(_:_:)`` along with the closure handling the WebSocket connection.

```swift
let app = Application(
Expand Down

0 comments on commit c0947d2

Please sign in to comment.