Skip to content

Commit 1abf82c

Browse files
committed
merge w/ main
2 parents 5ff0245 + e0ecdf0 commit 1abf82c

File tree

4 files changed

+101
-10
lines changed

4 files changed

+101
-10
lines changed

Sources/OpenAPIKit/Schema Object/JSONSchema.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ extension JSONSchema: VendorExtendable {
480480

481481
public func with(vendorExtensions: [String: AnyCodable]) -> JSONSchema {
482482
.init(
483-
warnings: warnings,
483+
warnings: warnings,
484484
schema: value.with(vendorExtensions: vendorExtensions)
485485
)
486486
}
@@ -1639,6 +1639,7 @@ extension JSONSchema {
16391639
minItems: Int? = nil,
16401640
maxItems: Int? = nil,
16411641
uniqueItems: Bool? = nil,
1642+
prefixItems: [JSONSchema]? = nil,
16421643
items: JSONSchema? = nil,
16431644
allowedValues: [AnyCodable]? = nil,
16441645
defaultValue: AnyCodable? = nil,
@@ -1669,6 +1670,7 @@ extension JSONSchema {
16691670
items: items,
16701671
maxItems: maxItems,
16711672
minItems: minItems,
1673+
prefixItems: prefixItems,
16721674
uniqueItems: uniqueItems
16731675
)
16741676
return .array(coreContext, arrayContext)
@@ -2154,22 +2156,22 @@ extension JSONSchema: Decodable {
21542156

21552157
self.value = value.with(vendorExtensions: extensions)
21562158
}
2157-
2159+
21582160
private static func decodeVenderExtensions(from decoder: Decoder) throws -> [String: AnyCodable] {
21592161
guard VendorExtensionsConfiguration.isEnabled(for: decoder) else {
21602162
return [:]
21612163
}
2162-
2164+
21632165
let decoded = try AnyCodable(from: decoder).value
2164-
2166+
21652167
guard (decoded as? [Any]) == nil else {
21662168
throw VendorExtensionDecodingError.selfIsArrayNotDict
21672169
}
21682170

21692171
guard let decodedAny = decoded as? [String: any Sendable] else {
21702172
throw VendorExtensionDecodingError.foundNonStringKeys
21712173
}
2172-
2174+
21732175
return decodedAny
21742176
.filter { $0.key.lowercased().starts(with: "x-") }
21752177
.mapValues(AnyCodable.init)

Sources/OpenAPIKit/Schema Object/JSONSchemaContext.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// JSONSchemaContext.swift
3-
//
3+
//
44
//
55
// Created by Mathew Polzin on 6/22/19.
66
//
@@ -318,7 +318,7 @@ extension JSONSchema.CoreContext: Equatable {
318318
&& lhs.externalDocs == rhs.externalDocs
319319
&& lhs.discriminator == rhs.discriminator
320320

321-
return step1
321+
return step1
322322
&& lhs.allowedValues == rhs.allowedValues
323323
&& lhs.defaultValue == rhs.defaultValue
324324
&& lhs.examples == rhs.examples
@@ -709,6 +709,11 @@ extension JSONSchema {
709709
/// Defaults to 0.
710710
public var minItems: Int { _minItems ?? 0 }
711711

712+
/// Multiple JSON Type Node that describe
713+
/// the types of the first few elements in the array.
714+
/// Useful for describing tuples.
715+
public let prefixItems: [JSONSchema]?
716+
712717
let _uniqueItems: Bool?
713718
/// Setting to true indicates all
714719
/// elements of the array are expected
@@ -719,11 +724,13 @@ extension JSONSchema {
719724
items: JSONSchema? = nil,
720725
maxItems: Int? = nil,
721726
minItems: Int? = nil,
727+
prefixItems: [JSONSchema]? = nil,
722728
uniqueItems: Bool? = nil
723729
) {
724730
self.items = items
725731
self.maxItems = maxItems
726732
self._minItems = minItems
733+
self.prefixItems = prefixItems
727734
self._uniqueItems = uniqueItems
728735
}
729736
}
@@ -1040,7 +1047,6 @@ extension JSONSchema.CoreContext: Decodable {
10401047
)
10411048
)
10421049
)
1043-
10441050
}
10451051
else if let types = try? container.decodeIfPresent([JSONType].self, forKey: .type) {
10461052
nullable = types.contains(JSONType.null)
@@ -1193,6 +1199,7 @@ extension JSONSchema.ArrayContext {
11931199
case maxItems
11941200
case minItems
11951201
case uniqueItems
1202+
case prefixItems
11961203
}
11971204
}
11981205

@@ -1203,6 +1210,7 @@ extension JSONSchema.ArrayContext: Encodable {
12031210
try container.encodeIfPresent(items, forKey: .items)
12041211
try container.encodeIfPresent(maxItems, forKey: .maxItems)
12051212
try container.encodeIfPresent(_minItems, forKey: .minItems)
1213+
try container.encodeIfPresent(prefixItems, forKey: .prefixItems)
12061214
try container.encodeIfPresent(_uniqueItems, forKey: .uniqueItems)
12071215
}
12081216
}
@@ -1214,6 +1222,7 @@ extension JSONSchema.ArrayContext: Decodable {
12141222
items = try container.decodeIfPresent(JSONSchema.self, forKey: .items)
12151223
maxItems = try container.decodeIfPresent(Int.self, forKey: .maxItems)
12161224
_minItems = try container.decodeIfPresent(Int.self, forKey: .minItems)
1225+
prefixItems = try container.decodeIfPresent([JSONSchema].self, forKey: .prefixItems)
12171226
_uniqueItems = try container.decodeIfPresent(Bool.self, forKey: .uniqueItems)
12181227
}
12191228
}

Sources/OpenAPIKitCore/Shared/ContentType.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// ContentType.swift
3-
//
3+
//
44
//
55
// Created by Mathew Polzin on 12/29/19.
66
//
@@ -111,6 +111,10 @@ public extension Shared.ContentType {
111111
static let form: Self = .init(.form)
112112
/// Graphics Interchange Format
113113
static let gif: Self = .init(.gif)
114+
/// geojson as defined in GeoJSON standard (RFC 7946)
115+
///
116+
/// see: https://datatracker.ietf.org/doc/html/rfc7946#section-12
117+
static let geojson: Self = .init(.geojson)
114118
static let html: Self = .init(.html)
115119
static let javascript: Self = .init(.javascript)
116120
/// JPEG image
@@ -131,9 +135,17 @@ public extension Shared.ContentType {
131135
/// OpenType font
132136
static let otf: Self = .init(.otf)
133137
static let pdf: Self = .init(.pdf)
138+
/// PNG image
139+
static let png: Self = .init(.png)
140+
/// Protocol Buffers
141+
///
142+
/// See: https://protobuf.dev/
143+
static let protobuf: Self = .init(.protobuf)
134144
/// RAR archive
135145
static let rar: Self = .init(.rar)
136146
static let rtf: Self = .init(.rtf)
147+
/// Scalable Vector Graphic (SVG)
148+
static let svg: Self = .init(.svg)
137149
/// Tape Archive (TAR)
138150
static let tar: Self = .init(.tar)
139151
/// TIF image
@@ -142,6 +154,8 @@ public extension Shared.ContentType {
142154
static let ttf: Self = .init(.ttf)
143155
/// Plaintext
144156
static let txt: Self = .init(.txt)
157+
/// Web Picture format (WEBP)
158+
static let webp: Self = .init(.webp)
145159
/// Web Open Font Format
146160
static let woff: Self = .init(.woff)
147161
/// Web Open Font Format
@@ -182,6 +196,10 @@ extension Shared.ContentType {
182196
case docx
183197
/// URL-encoded form data. See also: `multipartForm`.
184198
case form
199+
/// geojson as defined in GeoJSON standard (RFC 7946)
200+
///
201+
/// see: https://datatracker.ietf.org/doc/html/rfc7946#section-12
202+
case geojson
185203
/// Graphics Interchange Format
186204
case gif
187205
case html
@@ -204,9 +222,17 @@ extension Shared.ContentType {
204222
/// OpenType font
205223
case otf
206224
case pdf
225+
/// PNG image
226+
case png
227+
/// Protocol Buffers
228+
///
229+
/// See: https://protobuf.dev/
230+
case protobuf
207231
/// RAR archive
208232
case rar
209233
case rtf
234+
/// Scalable Vector Graphic (SVG)
235+
case svg
210236
/// Tape Archive (TAR)
211237
case tar
212238
/// TIF image
@@ -215,6 +241,8 @@ extension Shared.ContentType {
215241
case ttf
216242
/// Plaintext
217243
case txt
244+
/// Web Picture format (WEBP)
245+
case webp
218246
/// Web Open Font Format
219247
case woff
220248
/// Web Open Font Format
@@ -250,6 +278,7 @@ extension Shared.ContentType.Builtin: RawRepresentable {
250278
case .doc: return "application/msword"
251279
case .docx: return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
252280
case .form: return "application/x-www-form-urlencoded"
281+
case .geojson: return "application/geo+json"
253282
case .gif: return "image/gif"
254283
case .html: return "text/html"
255284
case .javascript: return "application/javascript"
@@ -263,12 +292,16 @@ extension Shared.ContentType.Builtin: RawRepresentable {
263292
case .multipartForm: return "multipart/form-data"
264293
case .otf: return "font/otf"
265294
case .pdf: return "application/pdf"
295+
case .png: return "image/png"
296+
case .protobuf: return "application/x-protobuf"
266297
case .rar: return "application/x-rar-compressed"
267298
case .rtf: return "application/rtf"
299+
case .svg: return "image/svg+xml"
268300
case .tar: return "application/x-tar"
269301
case .tif: return "image/tiff"
270302
case .ttf: return "font/ttf"
271303
case .txt: return "text/plain"
304+
case .webp: return "image/webp"
272305
case .woff: return "font/woff"
273306
case .woff2: return "font/woff2"
274307
case .xml: return "application/xml"
@@ -298,6 +331,7 @@ extension Shared.ContentType.Builtin: RawRepresentable {
298331
case "application/msword": self = .doc
299332
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": self = .docx
300333
case "application/x-www-form-urlencoded": self = .form
334+
case "application/geo+json": self = .geojson
301335
case "image/gif": self = .gif
302336
case "text/html": self = .html
303337
case "application/javascript": self = .javascript
@@ -311,12 +345,16 @@ extension Shared.ContentType.Builtin: RawRepresentable {
311345
case "multipart/form-data": self = .multipartForm
312346
case "font/otf": self = .otf
313347
case "application/pdf": self = .pdf
348+
case "image/png": self = .png
349+
case "application/x-protobuf": self = .protobuf
314350
case "application/x-rar-compressed": self = .rar
315351
case "application/rtf": self = .rtf
352+
case "image/svg+xml": self = .svg
316353
case "application/x-tar": self = .tar
317354
case "image/tiff": self = .tif
318355
case "font/ttf": self = .ttf
319356
case "text/plain": self = .txt
357+
case "image/webp": self = .webp
320358
case "font/woff": self = .woff
321359
case "font/woff2": self = .woff2
322360
case "application/xml": self = .xml

Tests/OpenAPIKitTests/Schema Object/SchemaFragmentTests.swift

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// SchemaFragmentTests.swift
3-
//
3+
//
44
//
55
// Created by Mathew Polzin on 4/21/20.
66
//
@@ -663,6 +663,30 @@ extension SchemaFragmentTests {
663663
}
664664
"""
665665
)
666+
667+
let t5 = JSONSchema.array(.init(), .init(items: .string, prefixItems: [.integer, .boolean]))
668+
669+
let encoded5 = try orderUnstableTestStringFromEncoding(of: t5)
670+
671+
assertJSONEquivalent(
672+
encoded5,
673+
"""
674+
{
675+
"items" : {
676+
"type" : "string"
677+
},
678+
"prefixItems" : [
679+
{
680+
"type" : "integer"
681+
},
682+
{
683+
"type" : "boolean"
684+
}
685+
],
686+
"type" : "array"
687+
}
688+
"""
689+
)
666690
}
667691

668692
func test_arrayDecode() throws {
@@ -724,6 +748,24 @@ extension SchemaFragmentTests {
724748
let decoded5 = try orderUnstableDecode(JSONSchema.self, from: t5)
725749

726750
XCTAssertEqual(decoded5, JSONSchema.array(.init(), .init(items: .string)))
751+
752+
let t6 =
753+
"""
754+
{
755+
"items" : {
756+
"type" : "string"
757+
},
758+
"prefixItems": [
759+
{
760+
"type" : "integer"
761+
}
762+
]
763+
}
764+
""".data(using: .utf8)!
765+
766+
let decoded6 = try orderUnstableDecode(JSONSchema.self, from: t6)
767+
768+
XCTAssertEqual(decoded6, JSONSchema.array(.init(), .init(items: .string, prefixItems: [.integer])))
727769
}
728770

729771
func test_objectEncode() throws {

0 commit comments

Comments
 (0)