Skip to content

Commit b0353de

Browse files
committed
Add Tag support for the kind field
1 parent c7a7220 commit b0353de

File tree

2 files changed

+102
-4
lines changed

2 files changed

+102
-4
lines changed

Sources/OpenAPIKit/Tag.swift

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ extension OpenAPI {
1919
public let externalDocs: ExternalDocumentation?
2020
/// The tag this tag is nested under.
2121
public let parent: String?
22+
/// A machine-readable string to categorize what sort of tag this is.
23+
/// Any string value can be used, but some common options are provided
24+
/// on OpenAPIKit's `Tag.Kind` type as static properties and more can
25+
/// be found in the public registry:
26+
/// https://spec.openapis.org/registry/tag-kind
27+
public let kind: Kind?
2228

2329
/// Dictionary of vendor extensions.
2430
///
@@ -35,25 +41,59 @@ extension OpenAPI {
3541
description: String? = nil,
3642
externalDocs: ExternalDocumentation? = nil,
3743
parent: String? = nil,
44+
kind: Kind? = nil,
3845
vendorExtensions: [String: AnyCodable] = [:]
3946
) {
4047
self.name = name
4148
self.summary = summary
4249
self.description = description
4350
self.externalDocs = externalDocs
4451
self.parent = parent
52+
self.kind = kind
4553
self.vendorExtensions = vendorExtensions
4654

4755
self.conditionalWarnings = [
4856
// If summary is non-nil, the document must be OAS version 3.2.0 or greater
4957
nonNilVersionWarning(fieldName: "summary", value: summary, minimumVersion: .v3_2_0),
5058
// If parent is non-nil, the document must be OAS version 3.2.0 or greater
51-
nonNilVersionWarning(fieldName: "parent", value: parent, minimumVersion: .v3_2_0)
59+
nonNilVersionWarning(fieldName: "parent", value: parent, minimumVersion: .v3_2_0),
60+
// If kind is non-nil, the document must be OAS version 3.2.0 or greater
61+
nonNilVersionWarning(fieldName: "kind", value: kind, minimumVersion: .v3_2_0)
5262
].compactMap { $0 }
5363
}
5464
}
5565
}
5666

67+
extension OpenAPI.Tag {
68+
public struct Kind : ExpressibleByStringLiteral, Codable, Equatable, Sendable {
69+
public let rawValue: String
70+
71+
public init(stringLiteral: String) {
72+
self.rawValue = stringLiteral
73+
}
74+
75+
public init(from decoder: Decoder) throws {
76+
let container = try decoder.singleValueContainer()
77+
self.rawValue = try container.decode(String.self)
78+
}
79+
80+
public func encode(to encoder: Encoder) throws {
81+
var container = encoder.singleValueContainer()
82+
83+
try container.encode(rawValue)
84+
}
85+
}
86+
}
87+
88+
extension OpenAPI.Tag.Kind {
89+
/// See https://spec.openapis.org/registry/tag-kind/audience.html
90+
public static let audience: OpenAPI.Tag.Kind = "audience"
91+
/// See https://spec.openapis.org/registry/tag-kind/badge.html
92+
public static let badge: OpenAPI.Tag.Kind = "badge"
93+
/// See https://spec.openapis.org/registry/tag-kind/nav.html
94+
public static let nav: OpenAPI.Tag.Kind = "nav"
95+
}
96+
5797
fileprivate func nonNilVersionWarning<Subject>(fieldName: String, value: Subject?, minimumVersion: OpenAPI.Document.Version) -> (any Condition, OpenAPI.Warning)? {
5898
value.map { _ in
5999
OpenAPI.Document.ConditionalWarnings.version(
@@ -70,6 +110,7 @@ extension OpenAPI.Tag: Equatable {
70110
&& lhs.description == rhs.description
71111
&& lhs.externalDocs == rhs.externalDocs
72112
&& lhs.parent == rhs.parent
113+
&& lhs.kind == rhs.kind
73114
&& lhs.vendorExtensions == rhs.vendorExtensions
74115
}
75116
}
@@ -91,6 +132,7 @@ extension OpenAPI.Tag : OpenAPISummarizable {
91132
description: description,
92133
externalDocs: externalDocs,
93134
parent: parent,
135+
kind: kind,
94136
vendorExtensions: vendorExtensions
95137
)
96138
}
@@ -103,6 +145,7 @@ extension OpenAPI.Tag : OpenAPISummarizable {
103145
description: description,
104146
externalDocs: externalDocs,
105147
parent: parent,
148+
kind: kind,
106149
vendorExtensions: vendorExtensions
107150
)
108151
}
@@ -124,6 +167,8 @@ extension OpenAPI.Tag: Encodable {
124167

125168
try container.encodeIfPresent(parent, forKey: .parent)
126169

170+
try container.encodeIfPresent(kind, forKey: .kind)
171+
127172
if VendorExtensionsConfiguration.isEnabled(for: encoder) {
128173
try encodeExtensions(to: &container)
129174
}
@@ -144,13 +189,17 @@ extension OpenAPI.Tag: Decodable {
144189

145190
parent = try container.decodeIfPresent(String.self, forKey: .parent)
146191

192+
kind = try container.decodeIfPresent(Kind.self, forKey: .kind)
193+
147194
vendorExtensions = try Self.extensions(from: decoder)
148195

149196
conditionalWarnings = [
150197
// If summary is non-nil, the document must be OAS version 3.2.0 or greater
151198
nonNilVersionWarning(fieldName: "summary", value: summary, minimumVersion: .v3_2_0),
152199
// If parent is non-nil, the document must be OAS version 3.2.0 or greater
153-
nonNilVersionWarning(fieldName: "parent", value: parent, minimumVersion: .v3_2_0)
200+
nonNilVersionWarning(fieldName: "parent", value: parent, minimumVersion: .v3_2_0),
201+
// If kind is non-nil, the document must be OAS version 3.2.0 or greater
202+
nonNilVersionWarning(fieldName: "kind", value: kind, minimumVersion: .v3_2_0)
154203
].compactMap { $0 }
155204
}
156205
}
@@ -162,6 +211,7 @@ extension OpenAPI.Tag {
162211
case description
163212
case externalDocs
164213
case parent
214+
case kind
165215
case extended(String)
166216

167217
static var allBuiltinKeys: [CodingKeys] {
@@ -170,7 +220,8 @@ extension OpenAPI.Tag {
170220
.summary,
171221
.description,
172222
.externalDocs,
173-
.parent
223+
.parent,
224+
.kind
174225
]
175226
}
176227

@@ -190,6 +241,8 @@ extension OpenAPI.Tag {
190241
self = .externalDocs
191242
case "parent":
192243
self = .parent
244+
case "kind":
245+
self = .kind
193246
default:
194247
self = .extendedKey(for: stringValue)
195248
}
@@ -207,6 +260,8 @@ extension OpenAPI.Tag {
207260
return "externalDocs"
208261
case .parent:
209262
return "parent"
263+
case .kind:
264+
return "kind"
210265
case .extended(let key):
211266
return key
212267
}

Tests/OpenAPIKitTests/TagTests.swift

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ final class TagTests: XCTestCase {
4545
parent: "otherTag"
4646
)
4747
XCTAssertEqual(t5.parent, "otherTag")
48+
49+
let t6 = OpenAPI.Tag(
50+
name: "hello",
51+
kind: .nav
52+
)
53+
XCTAssertEqual(t6.kind, .nav)
4854
}
4955
}
5056

@@ -177,6 +183,39 @@ extension TagTests {
177183
XCTAssertEqual(tag.conditionalWarnings.count, 1)
178184
}
179185

186+
func test_nameAndKind_encode() throws {
187+
let tag = OpenAPI.Tag(
188+
name: "hello",
189+
kind: .badge
190+
)
191+
let encodedTag = try orderUnstableTestStringFromEncoding(of: tag)
192+
193+
assertJSONEquivalent(
194+
encodedTag,
195+
"""
196+
{
197+
"kind" : "badge",
198+
"name" : "hello"
199+
}
200+
"""
201+
)
202+
}
203+
204+
func test_nameAndKind_decode() throws {
205+
let tagData =
206+
"""
207+
{
208+
"name": "hello",
209+
"kind": "audience"
210+
}
211+
""".data(using: .utf8)!
212+
213+
let tag = try orderUnstableDecode(OpenAPI.Tag.self, from: tagData)
214+
215+
XCTAssertEqual(tag, OpenAPI.Tag(name: "hello", kind: .audience))
216+
XCTAssertEqual(tag.conditionalWarnings.count, 1)
217+
}
218+
180219
func test_allFields_encode() throws {
181220
let tag = OpenAPI.Tag(
182221
name: "hello",
@@ -186,6 +225,7 @@ extension TagTests {
186225
url: URL(string: "http://google.com")!
187226
),
188227
parent: "otherTag",
228+
kind: "mytag",
189229
vendorExtensions: ["x-specialFeature": false]
190230
)
191231
let encodedTag = try orderUnstableTestStringFromEncoding(of: tag)
@@ -198,6 +238,7 @@ extension TagTests {
198238
"externalDocs" : {
199239
"url" : "http:\\/\\/google.com"
200240
},
241+
"kind" : "mytag",
201242
"name" : "hello",
202243
"parent" : "otherTag",
203244
"summary" : "sum",
@@ -218,6 +259,7 @@ extension TagTests {
218259
"url": "http://google.com"
219260
},
220261
"parent": "otherTag",
262+
"kind": "mytag",
221263
"x-specialFeature" : false
222264
}
223265
""".data(using: .utf8)!
@@ -232,9 +274,10 @@ extension TagTests {
232274
description: "world",
233275
externalDocs: .init(url: URL(string: "http://google.com")!),
234276
parent: "otherTag",
277+
kind: "mytag",
235278
vendorExtensions: ["x-specialFeature": false]
236279
)
237280
)
238-
XCTAssertEqual(tag.conditionalWarnings.count, 2)
281+
XCTAssertEqual(tag.conditionalWarnings.count, 3)
239282
}
240283
}

0 commit comments

Comments
 (0)