From 7799b106d70f649be45b37357c57cb0b798a8da9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Sat, 20 Dec 2025 10:26:49 -0600 Subject: [PATCH] add example object conditional warnings --- Sources/OpenAPIKit/Example/Example.swift | 43 +++++++++++++++++++++++- Tests/OpenAPIKitTests/ExampleTests.swift | 26 ++++++++++---- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/Sources/OpenAPIKit/Example/Example.swift b/Sources/OpenAPIKit/Example/Example.swift index 094a7ce1a..d1a123682 100644 --- a/Sources/OpenAPIKit/Example/Example.swift +++ b/Sources/OpenAPIKit/Example/Example.swift @@ -12,7 +12,7 @@ extension OpenAPI { /// OpenAPI Spec "Example Object" /// /// See [OpenAPI Example Object](https://spec.openapis.org/oas/v3.2.0.html#example-object). - public struct Example: Equatable, CodableVendorExtendable, Sendable { + public struct Example: HasConditionalWarnings, CodableVendorExtendable, Sendable { public let summary: String? public let description: String? @@ -27,6 +27,8 @@ extension OpenAPI { /// where the values are anything codable. public var vendorExtensions: [String: AnyCodable] + public let conditionalWarnings: [(any Condition, OpenAPI.Warning)] + public var dataValue: AnyCodable? { value?.dataValue } public var serializedValue: String? { value?.serializedValue } public var externalValue: URL? { value?.externalValue } @@ -49,6 +51,8 @@ extension OpenAPI { case nil: self.value = nil } self.vendorExtensions = vendorExtensions + + self.conditionalWarnings = self.value?.conditionalWarnings ?? [] } public init( @@ -65,6 +69,8 @@ extension OpenAPI { case nil: self.value = nil } self.vendorExtensions = vendorExtensions + + self.conditionalWarnings = self.value?.conditionalWarnings ?? [] } public init( @@ -77,6 +83,8 @@ extension OpenAPI { self.description = description self.value = value self.vendorExtensions = vendorExtensions + + self.conditionalWarnings = self.value?.conditionalWarnings ?? [] } public init( @@ -94,6 +102,8 @@ extension OpenAPI { self.value = nil } self.vendorExtensions = vendorExtensions + + self.conditionalWarnings = self.value?.conditionalWarnings ?? [] } public init( @@ -111,14 +121,43 @@ extension OpenAPI { self.value = nil } self.vendorExtensions = vendorExtensions + + self.conditionalWarnings = self.value?.conditionalWarnings ?? [] } } } +extension OpenAPI.Example: Equatable { + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.summary == rhs.summary + && lhs.description == rhs.description + && lhs.value == rhs.value + && lhs.vendorExtensions == rhs.vendorExtensions + } +} + extension OpenAPI.Example { public typealias Map = OrderedDictionary, OpenAPI.Example>> } +extension OpenAPI.Example.Value { + fileprivate var conditionalWarnings: [(any Condition, OpenAPI.Warning)] { + [ + nonNilVersionWarning(fieldName: "dataValue", value: dataValue, minimumVersion: .v3_2_0), + nonNilVersionWarning(fieldName: "serializedValue", value: serializedValue, minimumVersion: .v3_2_0) + ].compactMap { $0 } + } +} + +fileprivate func nonNilVersionWarning(fieldName: String, value: Subject?, minimumVersion: OpenAPI.Document.Version) -> (any Condition, OpenAPI.Warning)? { + value.map { _ in + OpenAPI.Document.ConditionalWarnings.version( + lessThan: minimumVersion, + doesNotSupport: "The Example Object \(fieldName) field" + ) + } +} + // MARK: - Either Convenience extension Either where A == OpenAPI.Reference, B == OpenAPI.Example { @available(*, deprecated, message: "This function populates the deprecated 'value' field, use .value(summary:description:dataValue:serializedValue:vendorExtensions:) or .value(summary:description:dataValue:externalValue:vendorExtensions:) instead.") @@ -268,6 +307,8 @@ extension OpenAPI.Example: Decodable { description = try container.decodeIfPresent(String.self, forKey: .description) vendorExtensions = try Self.extensions(from: decoder) + + conditionalWarnings = self.value?.conditionalWarnings ?? [] } } diff --git a/Tests/OpenAPIKitTests/ExampleTests.swift b/Tests/OpenAPIKitTests/ExampleTests.swift index 2b23f9687..407e2a51d 100644 --- a/Tests/OpenAPIKitTests/ExampleTests.swift +++ b/Tests/OpenAPIKitTests/ExampleTests.swift @@ -24,6 +24,7 @@ final class ExampleTests: XCTestCase { XCTAssertEqual(full1.legacyValue, .init(URL(string: "https://google.com")!)) XCTAssertEqual(full1.dataOrLegacyValue, .init(URL(string: "https://google.com")!)) XCTAssertEqual(full1.vendorExtensions["hello"]?.value as? String, "world") + XCTAssertEqual(full1.conditionalWarnings.count, 0) let full2 = OpenAPI.Example( summary: "hello", @@ -31,6 +32,13 @@ final class ExampleTests: XCTestCase { dataValue: .init("hello"), vendorExtensions: ["hello": "world"] ) + XCTAssertEqual(full2.summary, "hello") + XCTAssertEqual(full2.description, "world") + XCTAssertEqual(full2.value?.value, .init("hello")) + XCTAssertEqual(full2.dataValue, .init("hello")) + XCTAssertEqual(full2.dataOrLegacyValue, .init("hello")) + XCTAssertEqual(full2.vendorExtensions["hello"]?.value as? String, "world") + XCTAssertEqual(full2.conditionalWarnings.count, 1) let full3 = OpenAPI.Example( summary: "hello", @@ -43,25 +51,31 @@ final class ExampleTests: XCTestCase { XCTAssertEqual(full3.description, "world") XCTAssertEqual(full3.externalValue, URL(string: "https://google.com")!) XCTAssertEqual(full3.vendorExtensions["hello"]?.value as? String, "world") + XCTAssertEqual(full3.conditionalWarnings.count, 0) - XCTAssertEqual(full2.summary, "hello") - XCTAssertEqual(full2.description, "world") - XCTAssertEqual(full2.value?.value, .init("hello")) - XCTAssertEqual(full2.dataValue, .init("hello")) - XCTAssertEqual(full2.dataOrLegacyValue, .init("hello")) - XCTAssertEqual(full2.vendorExtensions["hello"]?.value as? String, "world") + let dataPlusSerialized = OpenAPI.Example( + summary: "hello", + dataValue: .init("hello"), + serializedValue: "hello" + ) + XCTAssertEqual(dataPlusSerialized.summary, "hello") + XCTAssertEqual(dataPlusSerialized.dataValue, .init("hello")) + XCTAssertEqual(dataPlusSerialized.serializedValue, "hello") + XCTAssertEqual(dataPlusSerialized.conditionalWarnings.count, 2) let small = OpenAPI.Example(serializedValue: "hello") XCTAssertNil(small.summary) XCTAssertNil(small.description) XCTAssertEqual(small.serializedValue, "hello") XCTAssertEqual(small.vendorExtensions, [:]) + XCTAssertEqual(small.conditionalWarnings.count, 1) let noValue = OpenAPI.Example() XCTAssertNil(noValue.summary) XCTAssertNil(noValue.description) XCTAssertNil(noValue.value) XCTAssertEqual(noValue.vendorExtensions, [:]) + XCTAssertEqual(noValue.conditionalWarnings.count, 0) let _ = OpenAPI.Example(legacyValue: .b(.init(["hi": "hello"]))) let _ = OpenAPI.Example(legacyValue: .b("hello"))