Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions Sources/_OpenAPIGeneratorCore/FeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
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,7 @@ 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,9 @@ 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)
}
}
10 changes: 9 additions & 1 deletion Sources/_OpenAPIGeneratorCore/Translator/FileTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ 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 +63,7 @@ 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,37 @@ 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
10 changes: 6 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()
let responseUUID = UUID()
transport = .init { (request: HTTPRequest, body: HTTPBody?, baseURL: URL, operationID: String) in
XCTAssertEqual(operationID, "listPets")
XCTAssertEqual(
Expand All @@ -44,12 +46,12 @@ 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 +69,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
10 changes: 6 additions & 4 deletions Tests/PetstoreConsumerTests/Test_Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ final class Test_Server: XCTestCase {
}

func testListPets_200() async throws {
let requestUUID = UUID()
let responseUUID = UUID()
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 +47,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 +56,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