Skip to content

Commit

Permalink
Merge pull request #3 from novr/chore/0.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
novr authored Nov 17, 2023
2 parents 4358824 + 6f3d936 commit 2838935
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 88 deletions.
21 changes: 15 additions & 6 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,35 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swift-cloud/Compute",
"state" : {
"revision" : "cc89dba27ade5713a2ac167fc752c4f4fe00439a",
"version" : "2.17.0"
"revision" : "542e212eb2f8eed7740205de30cc57e9e0845cfe",
"version" : "2.18.0"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto",
"state" : {
"revision" : "33a20e650c33f6d72d822d558333f2085effa3dc",
"version" : "2.5.0"
"revision" : "60f13f60c4d093691934dc6cfdf5f508ada1f894",
"version" : "2.6.0"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "99d066e29effa8845e4761dd3f2f831edfdf8925",
"version" : "1.0.0"
}
},
{
"identity" : "swift-openapi-runtime",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-openapi-runtime.git",
"state" : {
"revision" : "ef2b34c4eb3175988dd318df7849d2052f106466",
"version" : "0.2.5"
"revision" : "a51b3bd6f2151e9a6f792ca6937a7242c4758768",
"version" : "0.3.6"
}
}
],
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ let package = Package(
targets: ["OpenAPICompute"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-runtime.git", .upToNextMinor(from: "0.2.0")),
.package(url: "https://github.com/swift-cloud/Compute", from: "2.17.0"),
.package(url: "https://github.com/apple/swift-openapi-runtime.git", from: "0.3.6"),
.package(url: "https://github.com/swift-cloud/Compute", from: "2.18.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
154 changes: 76 additions & 78 deletions Sources/OpenAPICompute/ComputeTransport.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import OpenAPIRuntime
import HTTPTypes
import Compute

public final class ComputeTransport {
Expand All @@ -12,38 +13,36 @@ public final class ComputeTransport {

extension ComputeTransport: ServerTransport {
public func register(
_ handler: @escaping @Sendable (OpenAPIRuntime.Request, OpenAPIRuntime.ServerRequestMetadata) async throws -> OpenAPIRuntime.Response,
method: OpenAPIRuntime.HTTPMethod,
path: [OpenAPIRuntime.RouterPathComponent],
queryItemNames: Set<String>) throws {
router.on(
try Compute.HTTPMethod(method),
Self.makeComputePath(from: path)
_ handler: @Sendable @escaping (HTTPRequest, HTTPBody?, ServerRequestMetadata) async throws -> (HTTPResponse, HTTPBody?),
method: HTTPRequest.Method,
path: String
) throws {
router.on(
method,
path
) { computeRequest in
let request = try await OpenAPIRuntime.Request(computeRequest)
let request = try await HTTPTypes.HTTPRequest(computeRequest)
let body = try await OpenAPIRuntime.HTTPBody(computeRequest)
let requestMetadata = try OpenAPIRuntime.ServerRequestMetadata(
from: computeRequest,
forPath: path,
extractingQueryItemNamed: queryItemNames
forPath: path
)
return try await handler(request, requestMetadata)
return try await handler(request, body, requestMetadata)
}
}

/// Make compute path string from RouterPathComponent array
static func makeComputePath(from path: [OpenAPIRuntime.RouterPathComponent]) -> String {
path.map(\.computePathComponent).joined(separator: "/")
}
}

extension RouterPathComponent {
/// Return path component as String
var computePathComponent: String {
switch self {
case .constant(let string):
return string
case .parameter(let parameter):
return ":\(parameter)"
extension [PathComponent] {
init(_ path: String) {
self = path.split(
separator: "/",
omittingEmptySubsequences: false
).map { parameter in
if parameter.first == "{", parameter.last == "}" {
return .parameter(String(parameter.dropFirst().dropLast()))
} else {
return .constant(String(parameter))
}
}
}
}
Expand All @@ -52,17 +51,26 @@ extension Compute.Router {

@discardableResult
func on(
_ method: Compute.HTTPMethod,
_ method: HTTPRequest.Method,
_ path: String,
use closure: @escaping (IncomingRequest) async throws -> OpenAPIRuntime.Response
use closure: @escaping (IncomingRequest) async throws -> (HTTPTypes.HTTPResponse, OpenAPIRuntime.HTTPBody?)
) -> Compute.Router {
let handler: (IncomingRequest, OutgoingResponse) async throws -> Void = { request, response in
let result = try await closure(request)
response.status(result.statusCode)
for header in result.headerFields {
response.header(header.name, header.value)
response.status(result.0.status.code)
result.0.headerFields.forEach {
response.header($0.name.rawName, $0.value)
}
guard let body = result.1 else {
try await response.end()
return
}
switch body.length {
case let .known(length):
try await response.send(Data(collecting: body, upTo: length))
case .unknown:
try await response.send(Data(collecting: body, upTo: .max))
}
try await response.send(result.body)
}
switch method {
case .get:
Expand Down Expand Up @@ -98,54 +106,51 @@ enum ComputeTransportError: Error {
case missingRequiredPathParameter(String)
}

extension Compute.PathComponent {
init(_ pathComponent: OpenAPIRuntime.RouterPathComponent) {
switch pathComponent {
case .constant(let value): self = .constant(value)
case .parameter(let value): self = .parameter(value)
}
}
}

extension OpenAPIRuntime.Request {
extension HTTPTypes.HTTPRequest {
init(_ computeRequest: Compute.IncomingRequest) async throws {
let headerFields: [OpenAPIRuntime.HeaderField] = .init(computeRequest.headers)
let bodyData = try await computeRequest.body.data()
let method = try OpenAPIRuntime.HTTPMethod(computeRequest.method)
let headerFields: HTTPTypes.HTTPFields = .init(computeRequest.headers)
let method = try HTTPTypes.HTTPRequest.Method(computeRequest.method)
let queries = computeRequest.url.query.map { "?\($0)" } ?? ""

self.init(
path: computeRequest.url.path,
query: computeRequest.url.query,
method: method,
headerFields: headerFields,
body: bodyData
scheme: computeRequest.url.scheme,
authority: computeRequest.url.host(),
path: computeRequest.url.path() + queries,
headerFields: headerFields
)
}
}

extension OpenAPIRuntime.ServerRequestMetadata {
init(
from computeRequest: Compute.IncomingRequest,
forPath path: [RouterPathComponent],
extractingQueryItemNamed queryItemNames: Set<String>
) throws {
self.init(
pathParameters: try .init(from: computeRequest, forPath: path),
queryParameters: .init(from: computeRequest, queryItemNames: queryItemNames)
extension OpenAPIRuntime.HTTPBody {
convenience init(_ computeRequest: Compute.IncomingRequest) async throws {
let contentLength = computeRequest.headers.entries().first { $0.key == "content-length"}.map { Int($0.value) }
await self.init(
try computeRequest.body.data(),
length: contentLength?.map { .known($0) } ?? .unknown,
iterationBehavior: .single
)
}
}

extension Dictionary where Key == String, Value == String {
init(from computeRequest: Compute.IncomingRequest, forPath path: [RouterPathComponent]) throws {
let keysAndValues = try path.compactMap { item -> (String, String)? in
guard case let .parameter(name) = item else {
extension OpenAPIRuntime.ServerRequestMetadata {
init(from computeRequest: Compute.IncomingRequest, forPath path: String) throws {
self.init(pathParameters: try .init(from: computeRequest, forPath: path))
}
}

extension Dictionary<String, Substring> {
init(from computeRequest: Compute.IncomingRequest, forPath path: String) throws {
let keysAndValues = try [PathComponent](path).compactMap { component throws -> String? in
guard case let .parameter(parameter) = component else {
return nil
}
guard let value = computeRequest.pathParams.get(name) else {
throw ComputeTransportError.missingRequiredPathParameter(name)
return parameter
}.map { parameter -> (String, Substring) in
guard let value = computeRequest.searchParams[parameter] else {
throw ComputeTransportError.missingRequiredPathParameter(parameter)
}
return (name, value)
return (parameter, Substring(value))
}
let pathParameterDictionary = try Dictionary(keysAndValues, uniquingKeysWith: { _, _ in
throw ComputeTransportError.duplicatePathParameter(keysAndValues.map(\.0))
Expand All @@ -154,25 +159,18 @@ extension Dictionary where Key == String, Value == String {
}
}

extension Array where Element == URLQueryItem {
init(from computeRequest: Compute.IncomingRequest, queryItemNames: Set<String>) {
let queryParameters = queryItemNames.sorted().compactMap { name -> URLQueryItem? in
guard let value = computeRequest.searchParams[name] else {
extension HTTPTypes.HTTPFields {
init(_ headers: Compute.Headers) {
self.init(headers.entries().compactMap { name, value in
guard let name = HTTPField.Name(name) else {
return nil
}
return .init(name: name, value: value)
}
self = queryParameters
}
}

extension Array where Element == OpenAPIRuntime.HeaderField {
init(_ headers: Compute.Headers) {
self = headers.entries().map { .init(name: $0.key, value: $0.value) }
return HTTPField(name: name, value: value)
})
}
}

extension OpenAPIRuntime.HTTPMethod {
extension HTTPTypes.HTTPRequest.Method {
init(_ method: Compute.HTTPMethod) throws {
switch method {
case .get: self = .get
Expand All @@ -188,7 +186,7 @@ extension OpenAPIRuntime.HTTPMethod {
}

extension Compute.HTTPMethod {
init(_ method: OpenAPIRuntime.HTTPMethod) throws {
init(_ method: HTTPTypes.HTTPRequest.Method) throws {
switch method {
case .get: self = .get
case .put: self = .put
Expand Down
5 changes: 3 additions & 2 deletions Tests/OpenAPIComputeTests/ComputeTransportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import XCTest
@testable import OpenAPICompute
import Compute
import OpenAPIRuntime
import HTTPTypes

final class ComputeTransportTests: XCTestCase {

Expand Down Expand Up @@ -29,7 +30,7 @@ final class ComputeTransportTests: XCTestCase {
XCTAssertEqual(name, "TRACE")
}

try XCTAssert(function: OpenAPIRuntime.HTTPMethod.init(_:), behavesAccordingTo: [
try XCTAssert(function: HTTPTypes.HTTPRequest.Method.init(_:), behavesAccordingTo: [
(.get, .get),
(.put, .put),
(.post, .post),
Expand All @@ -38,7 +39,7 @@ final class ComputeTransportTests: XCTestCase {
(.head, .head),
(.patch, .patch),
])
try XCTAssertThrowsError(OpenAPIRuntime.HTTPMethod(.query)) {
try XCTAssertThrowsError(HTTPTypes.HTTPRequest.Method(.query)) {
guard case let ComputeTransportError.unsupportedHTTPMethod(name) = $0 else {
XCTFail()
return
Expand Down

0 comments on commit 2838935

Please sign in to comment.