Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Generator] Async bodies + swift-http-types adoption #245

Merged
merged 30 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
09b7fdb
[WIP] Adopt swift-http-types
czechboy0 Sep 4, 2023
85aa75f
Try streaming
czechboy0 Sep 4, 2023
2eb8cf2
Add more streaming examples
czechboy0 Sep 4, 2023
5cd2a8b
petstore consumer tests working again
czechboy0 Sep 5, 2023
7996f90
Add a streaming example
czechboy0 Sep 5, 2023
d0436f1
Add a server streaming example
czechboy0 Sep 5, 2023
463fd73
Add another server streaming example that uses the unfolding initiali…
czechboy0 Sep 5, 2023
25ebc93
Use Swift overloads directly
czechboy0 Sep 5, 2023
531e1e6
WIP
czechboy0 Sep 6, 2023
3e4ee23
Fix tests up
czechboy0 Sep 6, 2023
c45e971
Updates
czechboy0 Sep 7, 2023
d3737e0
Represent no responses as a nil HTTPBody in server transport and midd…
czechboy0 Sep 8, 2023
dc32180
Use the renamed method
czechboy0 Sep 9, 2023
fa5926f
Fixes
czechboy0 Sep 9, 2023
31545a1
Feedback: further cleanup of the HTTPBody API
czechboy0 Sep 12, 2023
d52e465
Remove unnecessary initializers
czechboy0 Sep 12, 2023
42596c2
Review feedback: make response body optional
czechboy0 Sep 13, 2023
f508302
Merge branch 'main' into hd-adopt-http-types
czechboy0 Sep 18, 2023
1e57593
Merge branch 'main' into hd-adopt-http-types
czechboy0 Sep 18, 2023
6b195be
WIP
czechboy0 Sep 25, 2023
66eb9ce
Merge remote-tracking branch 'apple/main' into hd-adopt-http-types
czechboy0 Sep 25, 2023
95141bb
WIP
czechboy0 Sep 25, 2023
0b766a4
Merge branch 'main' into hd-adopt-http-types
czechboy0 Sep 25, 2023
b0f99dc
Stop using the generated SPI
czechboy0 Sep 26, 2023
423ca43
Adapt to removed utils in runtime
czechboy0 Sep 27, 2023
5bd9375
Bump versions to 0.3.0
czechboy0 Sep 27, 2023
427fe7a
Merge branch 'main' into hd-adopt-http-types
czechboy0 Oct 2, 2023
89a5bdd
Only use Swift 5.8 APIs
czechboy0 Oct 2, 2023
d2c44d4
Bump versions in the example package
czechboy0 Oct 2, 2023
0d2e294
PR feedback: use TestClient instead of custom server implementations
czechboy0 Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Examples/GreetingService/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ let package = Package(
.macOS(.v13)
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/apple/swift-openapi-urlsession", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/swift-server/swift-openapi-vapor", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/apple/swift-openapi-urlsession", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/swift-server/swift-openapi-vapor", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/vapor/vapor", from: "4.76.0"),
],
targets: [
Expand Down
4 changes: 2 additions & 2 deletions IntegrationTest/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.3.0")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.0")),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ let package = Package(
// Tests-only: Runtime library linked by generated code, and also
// helps keep the runtime library new enough to work with the generated
// code.
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.2.4")),
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.0")),

// Build and preview docs
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
Expand Down
44 changes: 42 additions & 2 deletions Sources/PetstoreConsumerTestCore/Assertions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,58 @@
//===----------------------------------------------------------------------===//
import Foundation
import XCTest
import OpenAPIRuntime

public func XCTAssertEqualStringifiedData(
_ expression1: @autoclosure () throws -> Data,
_ expression1: @autoclosure () throws -> Data?,
_ expression2: @autoclosure () throws -> String,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line
) {
do {
let actualString = String(decoding: try expression1(), as: UTF8.self)
guard let value1 = try expression1() else {
XCTFail("First value is nil", file: file, line: line)
return
}
let actualString = String(decoding: value1, as: UTF8.self)
XCTAssertEqual(actualString, try expression2(), file: file, line: line)
} catch {
XCTFail(error.localizedDescription, file: file, line: line)
}
}

public func XCTAssertEqualStringifiedData<S: Sequence>(
_ expression1: @autoclosure () throws -> S?,
_ expression2: @autoclosure () throws -> String,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line
) where S.Element == UInt8 {
do {
guard let value1 = try expression1() else {
XCTFail("First value is nil", file: file, line: line)
return
}
let actualString = String(decoding: Array(value1), as: UTF8.self)
XCTAssertEqual(actualString, try expression2(), file: file, line: line)
} catch {
XCTFail(error.localizedDescription, file: file, line: line)
}
}

public func XCTAssertEqualStringifiedData(
_ expression1: @autoclosure () throws -> HTTPBody?,
_ expression2: @autoclosure () throws -> String,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line
) async throws {
let data: Data
if let body = try expression1() {
data = try await Data(collecting: body, upTo: .max)
} else {
data = .init()
}
XCTAssertEqualStringifiedData(data, try expression2(), message(), file: file, line: line)
}
63 changes: 21 additions & 42 deletions Sources/PetstoreConsumerTestCore/Common.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
//===----------------------------------------------------------------------===//
import OpenAPIRuntime
import Foundation
import HTTPTypes

public enum TestError: Swift.Error, LocalizedError, CustomStringConvertible, Sendable {
case noHandlerFound(method: HTTPMethod, path: [RouterPathComponent])
case noHandlerFound(method: HTTPRequest.Method, path: String)
case invalidURLString(String)
case unexpectedValue(any Sendable)
case unexpectedMissingRequestBody

public var description: String {
switch self {
case .noHandlerFound(let method, let path):
return "No handler found for method \(method.name) and path \(path.stringPath)"
return "No handler found for method \(method) and path \(path)"
case .invalidURLString(let string):
return "Invalid URL string: \(string)"
case .unexpectedValue(let value):
Expand All @@ -48,40 +49,31 @@ public extension Date {
}
}

public extension Array where Element == RouterPathComponent {
var stringPath: String {
map(\.description).joined(separator: "/")
}
}
public extension HTTPResponse {

public extension Response {
init(
statusCode: Int,
headers: [HeaderField] = [],
encodedBody: String
) {
self.init(
statusCode: statusCode,
headerFields: headers,
body: Data(encodedBody.utf8)
)
func withEncodedBody(_ encodedBody: String) throws -> (HTTPResponse, HTTPBody) {
(self, .init(encodedBody))
}

static var listPetsSuccess: Self {
.init(
statusCode: 200,
headers: [
.init(name: "content-type", value: "application/json")
],
encodedBody: #"""
static var listPetsSuccess: (HTTPResponse, HTTPBody) {
get throws {
try Self(
status: .ok,
headerFields: [
.contentType: "application/json"
]
)
.withEncodedBody(
#"""
[
{
"id": 1,
"name": "Fluffz"
}
]
"""#
)
)
}
}
}

Expand Down Expand Up @@ -111,21 +103,8 @@ public extension Data {
}
}

public extension Request {
init(
path: String,
query: String? = nil,
method: HTTPMethod,
headerFields: [HeaderField] = [],
encodedBody: String
) throws {
let body = Data(encodedBody.utf8)
self.init(
path: path,
query: query,
method: method,
headerFields: headerFields,
body: body
)
public extension HTTPRequest {
func withEncodedBody(_ encodedBody: String) -> (HTTPRequest, HTTPBody) {
czechboy0 marked this conversation as resolved.
Show resolved Hide resolved
(self, .init(encodedBody))
}
}
12 changes: 8 additions & 4 deletions Sources/PetstoreConsumerTestCore/TestClientTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
//===----------------------------------------------------------------------===//
import OpenAPIRuntime
import Foundation
import HTTPTypes

public struct TestClientTransport: ClientTransport {

public typealias CallHandler = @Sendable (Request, URL, String) async throws -> Response
public typealias CallHandler = @Sendable (HTTPRequest, HTTPBody?, URL, String) async throws -> (
HTTPResponse, HTTPBody?
)

public let callHandler: CallHandler

Expand All @@ -25,10 +28,11 @@ public struct TestClientTransport: ClientTransport {
}

public func send(
_ request: Request,
_ request: HTTPRequest,
body: HTTPBody?,
baseURL: URL,
operationID: String
) async throws -> Response {
try await callHandler(request, baseURL, operationID)
) async throws -> (HTTPResponse, HTTPBody?) {
try await callHandler(request, body, baseURL, operationID)
}
}
24 changes: 13 additions & 11 deletions Sources/PetstoreConsumerTestCore/TestServerTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@
//
//===----------------------------------------------------------------------===//
import OpenAPIRuntime
import HTTPTypes

public final class TestServerTransport: ServerTransport {

public struct OperationInputs: Equatable {
public var method: HTTPMethod
public var path: [RouterPathComponent]
public var queryItemNames: Set<String>
public var method: HTTPRequest.Method
public var path: String

public init(method: HTTPMethod, path: [RouterPathComponent], queryItemNames: Set<String>) {
public init(method: HTTPRequest.Method, path: String) {
self.method = method
self.path = path
self.queryItemNames = queryItemNames
}
}

public typealias Handler = @Sendable (Request, ServerRequestMetadata) async throws -> Response
public typealias Handler = @Sendable (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (
HTTPResponse, HTTPBody?
)

public struct Operation {
public var inputs: OperationInputs
Expand All @@ -43,14 +44,15 @@ public final class TestServerTransport: ServerTransport {
public private(set) var registered: [Operation] = []

public func register(
_ handler: @escaping Handler,
method: HTTPMethod,
path: [RouterPathComponent],
queryItemNames: Set<String>
_ handler: @Sendable @escaping (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (
HTTPResponse, HTTPBody?
),
method: HTTPRequest.Method,
path: String
) throws {
registered.append(
Operation(
inputs: .init(method: method, path: path, queryItemNames: queryItemNames),
inputs: .init(method: method, path: path),
closure: handler
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ struct FunctionSignatureDescription: Equatable, Codable {
var keywords: [FunctionKeyword] = []

/// The return type name of the function, such as `Int`.
var returnType: String? = nil
var returnType: Expression? = nil
}

/// A description of a function definition.
Expand Down Expand Up @@ -479,7 +479,7 @@ struct FunctionDescription: Equatable, Codable {
kind: FunctionKind,
parameters: [ParameterDescription] = [],
keywords: [FunctionKeyword] = [],
returnType: String? = nil,
returnType: Expression? = nil,
body: [CodeBlock]? = nil
) {
self.signature = .init(
Expand All @@ -505,7 +505,7 @@ struct FunctionDescription: Equatable, Codable {
kind: FunctionKind,
parameters: [ParameterDescription] = [],
keywords: [FunctionKeyword] = [],
returnType: String? = nil,
returnType: Expression? = nil,
body: [Expression]
) {
self.init(
Expand Down Expand Up @@ -858,6 +858,17 @@ struct OptionalChainingDescription: Equatable, Codable {
var referencedExpr: Expression
}

/// A description of a tuple.
///
/// For example: `(foo, bar)`.
struct TupleDescription: Equatable, Codable {

/// The member expressions.
///
/// For example, in `(foo, bar)`, `members` is `[foo, bar]`.
var members: [Expression]
}

/// A Swift expression.
indirect enum Expression: Equatable, Codable {

Expand Down Expand Up @@ -928,6 +939,11 @@ indirect enum Expression: Equatable, Codable {
///
/// For example, in `foo?`, `referencedExpr` is `foo`.
case optionalChaining(OptionalChainingDescription)

/// A tuple expression.
///
/// For example: `(foo, bar)`.
case tuple(TupleDescription)
}

/// A code block item, either a declaration or an expression.
Expand Down Expand Up @@ -1076,7 +1092,7 @@ extension Declaration {
kind: FunctionKind,
parameters: [ParameterDescription],
keywords: [FunctionKeyword] = [],
returnType: String? = nil,
returnType: Expression? = nil,
body: [CodeBlock]? = nil
) -> Self {
.function(
Expand Down Expand Up @@ -1432,6 +1448,15 @@ extension Expression {
func optionallyChained() -> Self {
.optionalChaining(.init(referencedExpr: self))
}

/// Returns a new tuple expression.
///
/// For example, in `(foo, bar)`, `members` is `[foo, bar]`.
/// - Parameter expressions: The member expressions.
/// - Returns: A tuple expression.
static func tuple(_ expressions: [Expression]) -> Self {
.tuple(.init(members: expressions))
}
}

extension MemberAccessDescription {
Expand Down
11 changes: 10 additions & 1 deletion Sources/_OpenAPIGeneratorCore/Renderer/TextBasedRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,13 @@ struct TextBasedRenderer: RendererProtocol {
renderedExpression(description.referencedExpr) + "?"
}

/// Renders the specified tuple expression.
func renderedTupleDescription(
_ description: TupleDescription
) -> String {
"(" + description.members.map(renderedExpression).joined(separator: ", ") + ")"
}

/// Renders the specified expression.
func renderedExpression(_ expression: Expression) -> String {
switch expression {
Expand Down Expand Up @@ -316,6 +323,8 @@ struct TextBasedRenderer: RendererProtocol {
return renderedInOutDescription(inOut)
case .optionalChaining(let optionalChaining):
return renderedOptionalChainingDescription(optionalChaining)
case .tuple(let tuple):
return renderedTupleDescription(tuple)
}
}

Expand Down Expand Up @@ -606,7 +615,7 @@ struct TextBasedRenderer: RendererProtocol {
}
if let returnType = signature.returnType {
words.append("->")
words.append(returnType)
words.append(renderedExpression(returnType))
}
return words.joinedWords()
}
Expand Down
Loading