Skip to content

Commit

Permalink
Enable documentation + Add a tutorial for Request injection (#16)
Browse files Browse the repository at this point in the history
* add basic docs configuration, write the first version of the tutorial
  • Loading branch information
MahdiBM authored Jan 16, 2024
1 parent f89da92 commit 0ae9850
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: 1
builder:
configs:
- documentation_targets:
- OpenAPIVapor
18 changes: 18 additions & 0 deletions Sources/OpenAPIVapor/Documentation.docc/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CDDefaultCodeListingLanguage</key>
<string>swift</string>
<key>CFBundleName</key>
<string>swift-openapi-vapor</string>
<key>CFBundleDisplayName</key>
<string>swift-openapi-vapor</string>
<key>CFBundleIdentifier</key>
<string>com.swift-server.OpenAPIVapor</string>
<key>CFBundlePackageType</key>
<string>DOCS</string>
<key>CDDefaultModuleKind</key>
<string>Tool</string>
</dict>
</plist>
12 changes: 12 additions & 0 deletions Sources/OpenAPIVapor/Documentation.docc/Swift-OpenAPI-Vapor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Swift OpenAPI Vapor

@Metadata {
@TechnologyRoot()
}

Generate Swift client and server code from an OpenAPI document.

## Topics

### Tutorials
- <doc:RequestInjection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@Tutorial(time: 10) {
@XcodeRequirement(title: "Swift 5.9", destination: "https://developer.apple.com/download/applications/")

@Intro(title: "Using `Request` in OpenAPI's APIProtocol") {
This tutorial guides you through passing down the `Request` to the OpenAPI Vapor route handler
}

@Section(title: "Adding the `swift-dependencies` library as a dependency of your app") {
This tutorial uses [swift-dependencies](https://github.com/pointfreeco/swift-dependencies) to inject the `Request` to the context of `APIProtocol`, using `TaskLocal` values.

@Steps {
@Step {
Make sure you add [swift-dependencies](https://github.com/pointfreeco/swift-dependencies) as a dependency of your app, as well as your target. See [this section](https://github.com/pointfreeco/swift-dependencies?tab=readme-ov-file#installation) for more info.
}
}
}

@Section(title: "Adding `request` to `DependencyValues`") {
You need to add a `request` variable as an extension on top of `DependencyValues` so in the next steps we can store each `Request` there.

@Steps {
@Step {
Create a `DependencyKey` for the `Request` value.
@Code(name: "+DependencyValues", file: request-injection.dependency-values.1.swift, reset: true)
}
@Step {
Add a `request` variable while using the `RequestKey` as the identifier to access `DependencyValues`'s underlying storage.
@Code(name: "+DependencyValues", file: request-injection.dependency-values.2.swift)
}
}

If you want to know more about how `swift-dependencies` works, refer to their [documentation](https://github.com/pointfreeco/swift-dependencies#documentation).
}

@Section(title: "Using a middleware to inject `Request` to the context of requests") {
Now you need to using an `AsyncMiddleware` which captures the request and injects it to the context of your requests using Swift's `TaskLocal`s.

@Steps {
@Step {
Create an `AsyncMiddleware`.
@Code(name: "OpenAPIRequestInjectionMiddleware", file: request-injection.middleware.1.swift)
}
@Step {
Use `swift-dependencies` APIs to inject the `request` to the context of the request.
@Code(name: "OpenAPIRequestInjectionMiddleware", file: request-injection.middleware.2.swift)
}
@Step {
Go to the file where you set up using the OpenAPI handler.
It should look like this.
@Code(name: "register-openapi-handler.swift", file: request-injection.using-middleware.1.swift)
}
@Step {
Change it so you're using the new `OpenAPIRequestInjectionMiddleware`.
Prefer to use this middleware as the last middleware for your routes, to avoid possible known problems with `TaskLocal` and Vapor's underlying implementation.
@Code(name: "register-openapi-handler.swift", file: request-injection.using-middleware.2.swift)
}
}
}

@Section(title: "Using `request` in your OpenAPI handler") {
Everything is now ready! You can use the `request` dependency value from your OpenAPI handler.

@Steps {
@Step {
Navigate to your APIProtocol implementation file.
It should look like this.
@Code(name: "MyAPIProtocolImpl.swift", file: request-injection.api-protocol.1.swift)
}
@Step {
Use `swift-dependencies` APIs to retrieve the `Request`.
Then you can use it freely like with normal Vapor route handlers.
@Code(name: "MyAPIProtocolImpl.swift", file: request-injection.api-protocol.2.swift)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@Tutorials(name: "Swift OpenAPI Vapor") {
@Intro(title: "Working with Swift OpenAPI Generator") {
Learn how to use *Swift OpenAPI Vapor* while still having access to the `Request` object.
}

@Chapter(name: "Swift OpenAPI Vapor Essentials") {
@Image(source: "image.png")

@TutorialReference(tutorial: "doc:RequestInjection")
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import OpenAPIVapor
import Dependencies

struct MyAPIProtocolImpl: APIProtocol {
func myOpenAPIEndpointFunction() async throws -> Operations.myOperation.Output {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import OpenAPIVapor
import Dependencies

struct MyAPIProtocolImpl: APIProtocol {
@Dependency(\.request) var request

func myOpenAPIEndpointFunction() async throws -> Operations.myOperation.Output {
/// Use `request` as if this is a normal Vapor endpoint function
request.logger.notice(
"Got a request!",
metadata: [
"request": .stringConvertible(request)
]
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Vapor
import Dependencies

extension DependencyValues {
private enum RequestKey: DependencyKey {
static var liveValue: Request {
fatalError("Value of type \(Value.self) is not registered in this context")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Vapor
import Dependencies

extension DependencyValues {
var request: Request {
get { self[RequestKey.self] }
set { self[RequestKey.self] = newValue }
}

private enum RequestKey: DependencyKey {
static var liveValue: Request {
fatalError("Value of type \(Value.self) is not registered in this context")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Vapor
import Dependencies

struct OpenAPIRequestInjectionMiddleware: AsyncMiddleware {
func respond(
to request: Request,
chainingTo responder: AsyncResponder
) async throws -> Response {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Vapor
import Dependencies

struct OpenAPIRequestInjectionMiddleware: AsyncMiddleware {
func respond(
to request: Request,
chainingTo responder: AsyncResponder
) async throws -> Response {
try await withDependencies {
$0.request = request
} operation: {
try await responder.respond(to: request)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Vapor
import OpenAPIRuntime
import OpenAPIVapor

...

let app = Vapor.Application()

let transport = VaporTransport(routesBuilder: app)

let handler = MyAPIProtocolImpl()

try handler.registerHandlers(on: transport, serverURL: Servers.server1())

...
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Vapor
import OpenAPIRuntime
import OpenAPIVapor

...

let app = Vapor.Application()

let requestInjectionMiddleware = OpenAPIRequestInjectionMiddleware()
let transport = VaporTransport(routesBuilder: app.grouped(requestInjectionMiddleware))

let handler = MyAPIProtocolImpl()

try handler.registerHandlers(on: transport, serverURL: Servers.server1())

...

0 comments on commit 0ae9850

Please sign in to comment.