Skip to content

Commit 40b7c04

Browse files
authored
Add option to include OpenAPI generator (#18)
1 parent 16b8ae3 commit 40b7c04

8 files changed

Lines changed: 109 additions & 5 deletions

Package.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,36 @@ let package = Package(
1111
],
1212
dependencies: [
1313
.package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0"),
14-
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0")
14+
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
15+
{{#HB_OPENAPI}}
16+
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.6.0"),
17+
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.7.0"),
18+
.package(url: "https://github.com/swift-server/swift-openapi-hummingbird.git", from: "2.0.1"),
19+
{{/HB_OPENAPI}}
1520
],
1621
targets: [
1722
.executableTarget(name: "{{HB_EXECUTABLE_NAME}}",
1823
dependencies: [
1924
.product(name: "ArgumentParser", package: "swift-argument-parser"),
2025
.product(name: "Hummingbird", package: "hummingbird"),
26+
{{#HB_OPENAPI}}
27+
.byName(name: "{{HB_EXECUTABLE_NAME}}API"),
28+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
29+
.product(name: "OpenAPIHummingbird", package: "swift-openapi-hummingbird"),
30+
{{/HB_OPENAPI}}
2131
],
2232
path: "Sources/App"
2333
),
34+
{{#HB_OPENAPI}}
35+
.target(
36+
name: "{{HB_EXECUTABLE_NAME}}API",
37+
dependencies: [
38+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime")
39+
],
40+
path: "Sources/AppAPI",
41+
plugins: [.plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator")],
42+
),
43+
{{/HB_OPENAPI}}
2444
.testTarget(name: "{{HB_EXECUTABLE_NAME}}Tests",
2545
dependencies: [
2646
.byName(name: "{{HB_EXECUTABLE_NAME}}"),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{#HB_OPENAPI}}
2+
import AppAPI
3+
import OpenAPIRuntime
4+
5+
struct APIImplementation: APIProtocol {
6+
func getHello(_ input: AppAPI.Operations.GetHello.Input) async throws -> AppAPI.Operations.GetHello.Output {
7+
return .ok(.init(body: .plainText("Hello!")))
8+
}
9+
}
10+
{{/HB_OPENAPI}}

Sources/App/Application+build.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import Hummingbird
22
import Logging
3+
{{#HB_OPENAPI}}
4+
import OpenAPIHummingbird
5+
{{/HB_OPENAPI}}
36

47
/// Application arguments protocol. We use a protocol so we can call
58
/// `buildApplication` inside Tests as well as in the App executable.
@@ -26,7 +29,7 @@ public func buildApplication(_ arguments: some AppArguments) async throws -> som
2629
.info
2730
return logger
2831
}()
29-
let router = buildRouter()
32+
let router = try buildRouter()
3033
let app = Application(
3134
router: router,
3235
configuration: .init(
@@ -39,16 +42,27 @@ public func buildApplication(_ arguments: some AppArguments) async throws -> som
3942
}
4043

4144
/// Build router
42-
func buildRouter() -> Router<AppRequestContext> {
45+
func buildRouter() throws -> Router<AppRequestContext> {
4346
let router = Router(context: AppRequestContext.self)
4447
// Add middleware
4548
router.addMiddleware {
4649
// logging middleware
4750
LogRequestsMiddleware(.info)
51+
{{#HB_OPENAPI}}
52+
// store request context in TaskLocal
53+
OpenAPIRequestContextMiddleware()
54+
{{/HB_OPENAPI}}
4855
}
56+
{{#HB_OPENAPI}}
57+
// Add OpenAPI handlers
58+
let api = APIImplementation()
59+
try api.registerHandlers(on: router)
60+
{{/HB_OPENAPI}}
61+
{{^HB_OPENAPI}}
4962
// Add default endpoint
5063
router.get("/") { _,_ in
5164
return "Hello!"
5265
}
66+
{{/HB_OPENAPI}}
5367
return router
5468
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{{#HB_OPENAPI}}
2+
import Hummingbird
3+
4+
extension AppRequestContext {
5+
@TaskLocal static var current: AppRequestContext?
6+
}
7+
8+
/// Middleware that adds the RequestContext as a TaskLocal so it is accessible from OpenAPI handlers.
9+
///
10+
/// This middleware should be the last in the middleware chain to ensure all edits to the RequestContext
11+
/// are passed to the Task Local
12+
struct OpenAPIRequestContextMiddleware: RouterMiddleware {
13+
typealias Context = AppRequestContext
14+
15+
func handle(_ request: Request, context: Context, next: (Request, Context) async throws -> Response) async throws -> Response {
16+
try await AppRequestContext.$current.withValue(context) {
17+
try await next(request, context)
18+
}
19+
}
20+
}
21+
{{/HB_OPENAPI}}

Sources/AppAPI/AppAPI.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{{#HB_OPENAPI}}
2+
// Empty swift file, needed for SwiftPM to recognize this is a Swift target
3+
{{/HB_OPENAPI}}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{#HB_OPENAPI}}
2+
generate:
3+
- types
4+
- server
5+
accessModifier: package
6+
namingStrategy: idiomatic
7+
{{/HB_OPENAPI}}

Sources/AppAPI/openapi.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{{#HB_OPENAPI}}
2+
openapi: '3.1.0'
3+
info:
4+
title: AppService
5+
version: 1.0.0
6+
servers:
7+
- url: https://example.com/api
8+
description: App service deployment.
9+
paths:
10+
/:
11+
get:
12+
operationId: getHello
13+
responses:
14+
'200':
15+
description: Hello!
16+
content:
17+
text/plain:
18+
schema:
19+
type: string
20+
{{/HB_OPENAPI}}

configure.sh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ run_mustache()
8484
if ! grep -q '[^[:space:]]' "$TEMP_FILE" ; then
8585
echo "Removing $FILE"
8686
rm "$TEMP_FILE"
87-
rm "$TARGET_FOLDER/$FILE"
87+
if [ "$IN_PLACE_EDIT" = true ]; then
88+
rm "$TARGET_FOLDER/$FILE"
89+
fi
8890
else
8991
echo "Copying $FILE"
9092
mv -f "$TEMP_FILE" "$TARGET_FOLDER/$FILE"
@@ -156,8 +158,15 @@ if [[ "$HB_EXECUTABLE_NAME" =~ [^a-zA-Z0-9_] ]]; then
156158
exitWithError "Invalid executable name: $HB_EXECUTABLE_NAME"
157159
fi
158160

161+
echo -n "Do you want to use the OpenAPI generator: "
162+
read_yes_no "no"
163+
if [[ "$READ_INPUT_RETURN" == "yes" ]]; then
164+
export HB_OPENAPI="yes"
165+
mkdir -p "$TARGET_FOLDER"/Sources/AppAPI
166+
fi
167+
159168
echo -n "Include Visual Studio Code snippets: "
160-
read_yes_no "yes"
169+
read_yes_no "no"
161170
if [[ "$READ_INPUT_RETURN" == "yes" ]]; then
162171
export HB_VSCODE_SNIPPETS="yes"
163172
fi

0 commit comments

Comments
 (0)