Skip to content

Commit 0ae9850

Browse files
authored
Enable documentation + Add a tutorial for Request injection (#16)
* add basic docs configuration, write the first version of the tutorial
1 parent f89da92 commit 0ae9850

14 files changed

Lines changed: 228 additions & 0 deletions

.spi.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: 1
2+
builder:
3+
configs:
4+
- documentation_targets:
5+
- OpenAPIVapor
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CDDefaultCodeListingLanguage</key>
6+
<string>swift</string>
7+
<key>CFBundleName</key>
8+
<string>swift-openapi-vapor</string>
9+
<key>CFBundleDisplayName</key>
10+
<string>swift-openapi-vapor</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>com.swift-server.OpenAPIVapor</string>
13+
<key>CFBundlePackageType</key>
14+
<string>DOCS</string>
15+
<key>CDDefaultModuleKind</key>
16+
<string>Tool</string>
17+
</dict>
18+
</plist>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Swift OpenAPI Vapor
2+
3+
@Metadata {
4+
@TechnologyRoot()
5+
}
6+
7+
Generate Swift client and server code from an OpenAPI document.
8+
9+
## Topics
10+
11+
### Tutorials
12+
- <doc:RequestInjection>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@Tutorial(time: 10) {
2+
@XcodeRequirement(title: "Swift 5.9", destination: "https://developer.apple.com/download/applications/")
3+
4+
@Intro(title: "Using `Request` in OpenAPI's APIProtocol") {
5+
This tutorial guides you through passing down the `Request` to the OpenAPI Vapor route handler
6+
}
7+
8+
@Section(title: "Adding the `swift-dependencies` library as a dependency of your app") {
9+
This tutorial uses [swift-dependencies](https://github.com/pointfreeco/swift-dependencies) to inject the `Request` to the context of `APIProtocol`, using `TaskLocal` values.
10+
11+
@Steps {
12+
@Step {
13+
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.
14+
}
15+
}
16+
}
17+
18+
@Section(title: "Adding `request` to `DependencyValues`") {
19+
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.
20+
21+
@Steps {
22+
@Step {
23+
Create a `DependencyKey` for the `Request` value.
24+
@Code(name: "+DependencyValues", file: request-injection.dependency-values.1.swift, reset: true)
25+
}
26+
@Step {
27+
Add a `request` variable while using the `RequestKey` as the identifier to access `DependencyValues`'s underlying storage.
28+
@Code(name: "+DependencyValues", file: request-injection.dependency-values.2.swift)
29+
}
30+
}
31+
32+
If you want to know more about how `swift-dependencies` works, refer to their [documentation](https://github.com/pointfreeco/swift-dependencies#documentation).
33+
}
34+
35+
@Section(title: "Using a middleware to inject `Request` to the context of requests") {
36+
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.
37+
38+
@Steps {
39+
@Step {
40+
Create an `AsyncMiddleware`.
41+
@Code(name: "OpenAPIRequestInjectionMiddleware", file: request-injection.middleware.1.swift)
42+
}
43+
@Step {
44+
Use `swift-dependencies` APIs to inject the `request` to the context of the request.
45+
@Code(name: "OpenAPIRequestInjectionMiddleware", file: request-injection.middleware.2.swift)
46+
}
47+
@Step {
48+
Go to the file where you set up using the OpenAPI handler.
49+
It should look like this.
50+
@Code(name: "register-openapi-handler.swift", file: request-injection.using-middleware.1.swift)
51+
}
52+
@Step {
53+
Change it so you're using the new `OpenAPIRequestInjectionMiddleware`.
54+
Prefer to use this middleware as the last middleware for your routes, to avoid possible known problems with `TaskLocal` and Vapor's underlying implementation.
55+
@Code(name: "register-openapi-handler.swift", file: request-injection.using-middleware.2.swift)
56+
}
57+
}
58+
}
59+
60+
@Section(title: "Using `request` in your OpenAPI handler") {
61+
Everything is now ready! You can use the `request` dependency value from your OpenAPI handler.
62+
63+
@Steps {
64+
@Step {
65+
Navigate to your APIProtocol implementation file.
66+
It should look like this.
67+
@Code(name: "MyAPIProtocolImpl.swift", file: request-injection.api-protocol.1.swift)
68+
}
69+
@Step {
70+
Use `swift-dependencies` APIs to retrieve the `Request`.
71+
Then you can use it freely like with normal Vapor route handlers.
72+
@Code(name: "MyAPIProtocolImpl.swift", file: request-injection.api-protocol.2.swift)
73+
}
74+
}
75+
}
76+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@Tutorials(name: "Swift OpenAPI Vapor") {
2+
@Intro(title: "Working with Swift OpenAPI Generator") {
3+
Learn how to use *Swift OpenAPI Vapor* while still having access to the `Request` object.
4+
}
5+
6+
@Chapter(name: "Swift OpenAPI Vapor Essentials") {
7+
@Image(source: "image.png")
8+
9+
@TutorialReference(tutorial: "doc:RequestInjection")
10+
}
11+
}
68 Bytes
Loading
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import OpenAPIVapor
2+
import Dependencies
3+
4+
struct MyAPIProtocolImpl: APIProtocol {
5+
func myOpenAPIEndpointFunction() async throws -> Operations.myOperation.Output {
6+
7+
}
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import OpenAPIVapor
2+
import Dependencies
3+
4+
struct MyAPIProtocolImpl: APIProtocol {
5+
@Dependency(\.request) var request
6+
7+
func myOpenAPIEndpointFunction() async throws -> Operations.myOperation.Output {
8+
/// Use `request` as if this is a normal Vapor endpoint function
9+
request.logger.notice(
10+
"Got a request!",
11+
metadata: [
12+
"request": .stringConvertible(request)
13+
]
14+
)
15+
}
16+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Vapor
2+
import Dependencies
3+
4+
extension DependencyValues {
5+
private enum RequestKey: DependencyKey {
6+
static var liveValue: Request {
7+
fatalError("Value of type \(Value.self) is not registered in this context")
8+
}
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Vapor
2+
import Dependencies
3+
4+
extension DependencyValues {
5+
var request: Request {
6+
get { self[RequestKey.self] }
7+
set { self[RequestKey.self] = newValue }
8+
}
9+
10+
private enum RequestKey: DependencyKey {
11+
static var liveValue: Request {
12+
fatalError("Value of type \(Value.self) is not registered in this context")
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)