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

UUID type support #627

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions Sources/_OpenAPIGeneratorCore/FeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
public enum FeatureFlag: String, Hashable, Codable, CaseIterable, Sendable {
// needs to be here for the enum to compile
case empty
/// UUID support
///
/// Enable interpretation of `type: string, format: uuid` as `Foundation.UUID` typed data.
case uuidSupport
}

/// A set of enabled feature flags.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ enum Constants {
ImportDescription(moduleName: Constants.Import.runtime, spi: "Generated"),
ImportDescription(
moduleName: "Foundation",
moduleTypes: ["struct Foundation.URL", "struct Foundation.Data", "struct Foundation.Date"],
moduleTypes: [
"struct Foundation.URL", "struct Foundation.Data", "struct Foundation.Date",
"struct Foundation.UUID",
],
preconcurrency: .onOS(["Linux"])
),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ import OpenAPIKit

extension FileTranslator {
// Add helpers for reading feature flags below.

/// A boolean value indicating whether the `uuid` format on schemas should be followed.
var supportUUIDFormat: Bool { config.featureFlags.contains(.uuidSupport) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ protocol FileTranslator {
extension FileTranslator {

/// A new context from the file translator.
var context: TranslatorContext { TranslatorContext(asSwiftSafeName: { $0.safeForSwiftCode }) }
var context: TranslatorContext {
TranslatorContext(asSwiftSafeName: { $0.safeForSwiftCode }, enableUUIDSupport: supportUUIDFormat)
}
}

/// A set of configuration values for concrete file translators.
Expand All @@ -58,4 +60,6 @@ struct TranslatorContext {
/// - Parameter string: The string to convert to be safe for Swift.
/// - Returns: A Swift-safe version of the input string.
var asSwiftSafeName: (String) -> String
/// A variable that indicates the presence of the `uuidSupport` feature flag.
var enableUUIDSupport: Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ extension TypeName {
/// Returns the type name for the URL type.
static var url: Self { .foundation("URL") }

/// Returns the type name for the UUID type.
static var uuid: Self { .foundation("UUID") }

/// Returns the type name for the DecodingError type.
static var decodingError: Self { .swift("DecodingError") }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ struct TypeMatcher {
default:
switch core.format {
case .dateTime: typeName = .date
case .uuid where context.enableUUIDSupport: typeName = .uuid
default: typeName = .string
}
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/OpenAPIGeneratorCoreTests/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Test_Core: XCTestCase {
func makeTranslator(
components: OpenAPI.Components = .noComponents,
diagnostics: any DiagnosticCollector = PrintingDiagnosticCollector(),
featureFlags: FeatureFlags = []
featureFlags: FeatureFlags = [.uuidSupport]
) -> TypesFileTranslator {
makeTypesTranslator(components: components, diagnostics: diagnostics, featureFlags: featureFlags)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ final class Test_OperationDescription: Test_Core {
endpoint: endpoint,
pathParameters: pathItem.parameters,
components: .init(),
context: .init(asSwiftSafeName: { $0 })
context: .init(asSwiftSafeName: { $0 }, enableUUIDSupport: true)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class Test_TypeMatcher: Test_Core {
(.string(contentEncoding: .base64), "OpenAPIRuntime.Base64EncodedData"),
(.string(.init(format: .date), .init()), "Swift.String"),
(.string(.init(format: .dateTime), .init()), "Foundation.Date"),
(.string(.init(format: .uuid), .init()), "Foundation.UUID"),

(.integer, "Swift.Int"), (.integer(.init(format: .int32), .init()), "Swift.Int32"),
(.integer(.init(format: .int64), .init()), "Swift.Int64"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class FileBasedReferenceTests: XCTestCase {
#endif
}

func testPetstore() throws { try _test(referenceProject: .init(name: .petstore)) }
func testPetstore() throws { try _test(referenceProject: .init(name: .petstore), featureFlags: [.uuidSupport]) }

// MARK: - Private

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ paths:
required: true
schema:
type: string
format: uuid
My-Tracing-Header:
$ref: '#/components/headers/TracingHeader'
content:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
@preconcurrency import struct Foundation.URL
@preconcurrency import struct Foundation.Data
@preconcurrency import struct Foundation.Date
@preconcurrency import struct Foundation.UUID
#else
import struct Foundation.URL
import struct Foundation.Data
import struct Foundation.Date
import struct Foundation.UUID
#endif
import HTTPTypes
/// Service for managing pet metadata.
Expand Down Expand Up @@ -107,7 +109,7 @@ public struct Client: APIProtocol {
My_hyphen_Response_hyphen_UUID: try converter.getRequiredHeaderFieldAsURI(
in: response.headerFields,
name: "My-Response-UUID",
as: Swift.String.self
as: Foundation.UUID.self
),
My_hyphen_Tracing_hyphen_Header: try converter.getOptionalHeaderFieldAsURI(
in: response.headerFields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
@preconcurrency import struct Foundation.URL
@preconcurrency import struct Foundation.Data
@preconcurrency import struct Foundation.Date
@preconcurrency import struct Foundation.UUID
#else
import struct Foundation.URL
import struct Foundation.Data
import struct Foundation.Date
import struct Foundation.UUID
#endif
import HTTPTypes
extension APIProtocol {
Expand Down Expand Up @@ -199,7 +201,7 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol {
My_hyphen_Request_hyphen_UUID: try converter.getOptionalHeaderFieldAsURI(
in: request.headerFields,
name: "My-Request-UUID",
as: Swift.String.self
as: Foundation.UUID.self
),
accept: try converter.extractAcceptHeaderIfPresent(in: request.headerFields)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
@preconcurrency import struct Foundation.URL
@preconcurrency import struct Foundation.Data
@preconcurrency import struct Foundation.Date
@preconcurrency import struct Foundation.UUID
#else
import struct Foundation.URL
import struct Foundation.Data
import struct Foundation.Date
import struct Foundation.UUID
#endif
/// A type that performs HTTP operations defined by the OpenAPI document.
public protocol APIProtocol: Sendable {
Expand Down Expand Up @@ -1820,15 +1822,15 @@ public enum Operations {
/// Request identifier
///
/// - Remark: Generated from `#/paths/pets/GET/header/My-Request-UUID`.
public var My_hyphen_Request_hyphen_UUID: Swift.String?
public var My_hyphen_Request_hyphen_UUID: Foundation.UUID?
public var accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.listPets.AcceptableContentType>]
/// Creates a new `Headers`.
///
/// - Parameters:
/// - My_hyphen_Request_hyphen_UUID: Request identifier
/// - accept:
public init(
My_hyphen_Request_hyphen_UUID: Swift.String? = nil,
My_hyphen_Request_hyphen_UUID: Foundation.UUID? = nil,
accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.listPets.AcceptableContentType>] = .defaultValues()
) {
self.My_hyphen_Request_hyphen_UUID = My_hyphen_Request_hyphen_UUID
Expand Down Expand Up @@ -1856,7 +1858,7 @@ public enum Operations {
/// Response identifier
///
/// - Remark: Generated from `#/paths/pets/GET/responses/200/headers/My-Response-UUID`.
public var My_hyphen_Response_hyphen_UUID: Swift.String
public var My_hyphen_Response_hyphen_UUID: Foundation.UUID
/// A description here.
///
/// - Remark: Generated from `#/paths/pets/GET/responses/200/headers/My-Tracing-Header`.
Expand All @@ -1867,7 +1869,7 @@ public enum Operations {
/// - My_hyphen_Response_hyphen_UUID: Response identifier
/// - My_hyphen_Tracing_hyphen_Header: A description here.
public init(
My_hyphen_Response_hyphen_UUID: Swift.String,
My_hyphen_Response_hyphen_UUID: Foundation.UUID,
My_hyphen_Tracing_hyphen_Header: Components.Headers.TracingHeader? = nil
) {
self.My_hyphen_Response_hyphen_UUID = My_hyphen_Response_hyphen_UUID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,36 @@ final class SnippetBasedReferenceTests: XCTestCase {
"""
)
}
func testComponentsSchemasUUID() throws {
try self.assertSchemasTranslation(
featureFlags: [.uuidSupport],
"""
schemas:
MyUUID:
type: string
format: uuid
""",
"""
public enum Schemas {
public typealias MyUUID = Foundation.UUID
}
"""
)
// Without UUID support, the schema will be translated as a string
try self.assertSchemasTranslation(
"""
schemas:
MyUUID:
type: string
format: uuid
""",
"""
public enum Schemas {
public typealias MyUUID = Swift.String
}
"""
)
}

func testComponentsSchemasBase64() throws {
try self.assertSchemasTranslation(
Expand Down
4 changes: 0 additions & 4 deletions Tests/PetstoreConsumerTests/Common.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
import XCTest
import HTTPTypes

extension Operations.listPets.Output {
static var success: Self { .ok(.init(headers: .init(My_hyphen_Response_hyphen_UUID: "abcd"), body: .json([]))) }
}

extension HTTPRequest {
/// Initializes an HTTP request with the specified path, HTTP method, and header fields.
///
Expand Down
13 changes: 9 additions & 4 deletions Tests/PetstoreConsumerTests/Test_Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ final class Test_Client: XCTestCase {
}

func testListPets_200() async throws {
let requestUUID = UUID(uuidString: "da6811e6-112f-494e-8bdd-7f8b2367cb66")!
let responseUUID = UUID(uuidString: "b1c601c1-8963-460b-9fe4-fda2f73da64f")!
transport = .init { (request: HTTPRequest, body: HTTPBody?, baseURL: URL, operationID: String) in
XCTAssertEqual(operationID, "listPets")
XCTAssertEqual(
Expand All @@ -44,12 +46,15 @@ final class Test_Client: XCTestCase {
)
XCTAssertEqual(baseURL.absoluteString, "/api")
XCTAssertEqual(request.method, .get)
XCTAssertEqual(request.headerFields, [.accept: "application/json", .init("My-Request-UUID")!: "abcd-1234"])
XCTAssertEqual(
request.headerFields,
[.accept: "application/json", .init("My-Request-UUID")!: requestUUID.uuidString]
)
XCTAssertNil(body)
return try HTTPResponse(
status: .ok,
headerFields: [
.contentType: "application/json", .init("my-response-uuid")!: "abcd",
.contentType: "application/json", .init("my-response-uuid")!: responseUUID.uuidString,
.init("my-tracing-header")!: "1234",
]
)
Expand All @@ -67,14 +72,14 @@ final class Test_Client: XCTestCase {
let response = try await client.listPets(
.init(
query: .init(limit: 24, habitat: .water, feeds: [.herbivore, .carnivore], since: .test),
headers: .init(My_hyphen_Request_hyphen_UUID: "abcd-1234")
headers: .init(My_hyphen_Request_hyphen_UUID: requestUUID)
)
)
guard case let .ok(value) = response else {
XCTFail("Unexpected response: \(response)")
return
}
XCTAssertEqual(value.headers.My_hyphen_Response_hyphen_UUID, "abcd")
XCTAssertEqual(value.headers.My_hyphen_Response_hyphen_UUID, responseUUID)
XCTAssertEqual(value.headers.My_hyphen_Tracing_hyphen_Header, "1234")
switch value.body {
case .json(let pets): XCTAssertEqual(pets, [.init(id: 1, name: "Fluffz")])
Expand Down
13 changes: 9 additions & 4 deletions Tests/PetstoreConsumerTests/Test_Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ final class Test_Server: XCTestCase {
}

func testListPets_200() async throws {
let requestUUID = UUID(uuidString: "da6811e6-112f-494e-8bdd-7f8b2367cb66")!
let responseUUID = UUID(uuidString: "b1c601c1-8963-460b-9fe4-fda2f73da64f")!
client = .init(listPetsBlock: { input in
XCTAssertEqual(input.query.limit, 24)
XCTAssertEqual(input.query.habitat, .water)
XCTAssertEqual(input.query.since, .test)
XCTAssertEqual(input.query.feeds, [.carnivore, .herbivore])
XCTAssertEqual(input.headers.My_hyphen_Request_hyphen_UUID, "abcd-1234")
XCTAssertEqual(input.headers.My_hyphen_Request_hyphen_UUID, requestUUID)
return .ok(
.init(
headers: .init(My_hyphen_Response_hyphen_UUID: "abcd", My_hyphen_Tracing_hyphen_Header: "1234"),
headers: .init(
My_hyphen_Response_hyphen_UUID: responseUUID,
My_hyphen_Tracing_hyphen_Header: "1234"
),
body: .json([.init(id: 1, name: "Fluffz")])
)
)
Expand All @@ -45,7 +50,7 @@ final class Test_Server: XCTestCase {
.init(
soar_path: "/api/pets?limit=24&habitat=water&feeds=carnivore&feeds=herbivore&since=\(Date.testString)",
method: .get,
headerFields: [.init("My-Request-UUID")!: "abcd-1234"]
headerFields: [.init("My-Request-UUID")!: requestUUID.uuidString]
),
nil,
.init()
Expand All @@ -54,7 +59,7 @@ final class Test_Server: XCTestCase {
XCTAssertEqual(
response.headerFields,
[
.init("My-Response-UUID")!: "abcd", .init("My-Tracing-Header")!: "1234",
.init("My-Response-UUID")!: responseUUID.uuidString, .init("My-Tracing-Header")!: "1234",
.contentType: "application/json; charset=utf-8", .contentLength: "47",
]
)
Expand Down