Skip to content

Commit

Permalink
Merge pull request #172 from ainame/ai/multipart-formdata
Browse files Browse the repository at this point in the history
Support multipart/form-data as opt-in feature
  • Loading branch information
ainame committed Dec 14, 2022
2 parents 2084450 + 23d4a39 commit da8730b
Show file tree
Hide file tree
Showing 45 changed files with 750 additions and 59 deletions.
15 changes: 15 additions & 0 deletions Docs/ConfigOptions.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Sources/CreateAPI/Generator/Generator+DataTypes.swift
Expand Up @@ -7,6 +7,8 @@ extension Generator {
switch format {
case .byte:
return builtInType("Data", format: "byte", overrides: options.dataTypes.string)
case .binary:
return builtInType("Data", format: "binary", overrides: options.dataTypes.string)
case .date where options.useNaiveDate:
setNaiveDateNeeded()
return .builtin("NaiveDate")
Expand All @@ -20,7 +22,7 @@ extension Generator {
return builtInType("UUID", format: "uuid", overrides: options.dataTypes.string)
case .other(let format):
return builtInType("String", format: format, overrides: options.dataTypes.string)
case .generic, .binary, .password:
case .generic, .password:
return .builtin("String")
}
}
Expand Down
15 changes: 11 additions & 4 deletions Sources/CreateAPI/Generator/Generator+Paths.swift
Expand Up @@ -683,7 +683,17 @@ extension Generator {
return nil
}

if let (content, contentType) = firstContent(for: [.json, .jsonapi, .other("application/scim+json"), .other("application/json"), .form]) {
var structuredRequestBodySupportedTypes: [OpenAPI.ContentType] = [
.json,
.jsonapi,
.other("application/scim+json"),
.other("application/json"),
.form
]
if !options.paths.useDataForMultipartFormDataRequestBody {
structuredRequestBodySupportedTypes.append(.multipartForm)
}
if let (content, contentType) = firstContent(for: structuredRequestBodySupportedTypes) {
let schema: JSONSchema
switch content.schema {
case .a(let reference):
Expand All @@ -708,9 +718,6 @@ extension Generator {
}
return BodyType(type: property.type.name, nested: property.nested)
}
if firstContent(for: [.multipartForm]) != nil {
return BodyType("Data") // Currently isn't supported
}
if firstContent(for: [.css, .csv, .form, .html, .javascript, .txt, .xml, .yaml, .anyText, .other("application/jwt"), .other("image/svg+xml"), .other("text/xml"), .other("plain/text")]) != nil {
return BodyType("String")
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/CreateOptions/ConfigOptions.swift
Expand Up @@ -672,6 +672,14 @@ public struct ConfigOptions: ParsableConfiguration {
///
/// </details>
@Option public var filenameTemplate: String = "%0.swift"

/// If `false`, CreateAPI generates request body structures for "multipart/form-data" format just like it would for "application/json".
/// Otherwise the `body` of the generated `Request` will use `Data`. The default value is `true`.
///
/// When using Get and it's `APIClient`, because Multipart Form Data isn't supported from the `Request` `body` property, it is best to leave this option set to `true`.
/// If however you have implemented your API Client, and you prefer to use structured `Codable` types to encode a Multipart Form Data request body, setting this value to `false` can be more convenient.
/// You might also need to use the [`dataTypes`](#datatypes) option to customise the type used to represent `binary` data.
@Option public var useDataForMultipartFormDataRequestBody: Bool = true
}

@Option public var rename: Rename
Expand Down
13 changes: 13 additions & 0 deletions Tests/CreateAPITests/GenerateOptionsTests.swift
Expand Up @@ -388,6 +388,19 @@ final class GenerateOptionsTests: GenerateTestCase {
"""
)
}

func testEdgecasesMultipartFormdata() throws {
try snapshot(
spec: .edgecases,
name: "edgecases-multipart-formdata",
configuration: """
generate: ["paths"]
paths:
useDataForMultipartFormDataRequestBody: false
"""
)
}


func testEdgecasesGenerateCodingKeys() throws {
try snapshot(
Expand Down
Expand Up @@ -13,13 +13,13 @@ struct FormatTest: Codable {
var double: Double?
var string: String?
var byte: Data
var binary: String?
var binary: Data?
var date: NaiveDate
var dateTime: Date?
var uuid: UUID?
var password: String

init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: String? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: Data? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand All @@ -45,7 +45,7 @@ struct FormatTest: Codable {
self.double = try values.decodeIfPresent(Double.self, forKey: "double")
self.string = try values.decodeIfPresent(String.self, forKey: "string")
self.byte = try values.decode(Data.self, forKey: "byte")
self.binary = try values.decodeIfPresent(String.self, forKey: "binary")
self.binary = try values.decodeIfPresent(Data.self, forKey: "binary")
self.date = try values.decode(NaiveDate.self, forKey: "date")
self.dateTime = try values.decodeIfPresent(Date.self, forKey: "dateTime")
self.uuid = try values.decodeIfPresent(UUID.self, forKey: "uuid")
Expand Down
Expand Up @@ -77,7 +77,7 @@ extension Paths {
/// None
var byte: Data
/// None
var binary: String?
var binary: Data?
/// None
var date: NaiveDate?
/// None
Expand All @@ -87,7 +87,7 @@ extension Paths {
/// None
var callback: String?

init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: String? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: Data? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -13,13 +13,13 @@ public struct FormatTest: Codable {
public var double: Double?
public var string: String?
public var byte: Data
public var binary: String?
public var binary: Data?
public var date: NaiveDate
public var dateTime: Date?
public var uuid: UUID?
public var password: String

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: String? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: Data? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -77,7 +77,7 @@ extension Paths {
/// None
public var byte: Data
/// None
public var binary: String?
public var binary: Data?
/// None
public var date: NaiveDate?
/// None
Expand All @@ -87,7 +87,7 @@ extension Paths {
/// None
public var callback: String?

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: String? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: Data? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -13,13 +13,13 @@ public struct FormatTest: Codable {
public var double: Double?
public var string: String?
public var byte: String
public var binary: String?
public var binary: Data?
public var date: NaiveDate
public var dateTime: AnyJSON?
public var uuid: UUID?
public var password: String

public init(integer: Int? = nil, int32: Double? = nil, int64: Int? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: String, binary: String? = nil, date: NaiveDate, dateTime: AnyJSON? = nil, uuid: UUID? = nil, password: String) {
public init(integer: Int? = nil, int32: Double? = nil, int64: Int? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: String, binary: Data? = nil, date: NaiveDate, dateTime: AnyJSON? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand All @@ -45,7 +45,7 @@ public struct FormatTest: Codable {
self.double = try values.decodeIfPresent(Double.self, forKey: "double")
self.string = try values.decodeIfPresent(String.self, forKey: "string")
self.byte = try values.decode(String.self, forKey: "byte")
self.binary = try values.decodeIfPresent(String.self, forKey: "binary")
self.binary = try values.decodeIfPresent(Data.self, forKey: "binary")
self.date = try values.decode(NaiveDate.self, forKey: "date")
self.dateTime = try values.decodeIfPresent(AnyJSON.self, forKey: "dateTime")
self.uuid = try values.decodeIfPresent(UUID.self, forKey: "uuid")
Expand Down
Expand Up @@ -76,7 +76,7 @@ extension Paths {
/// None
public var byte: String
/// None
public var binary: String?
public var binary: Data?
/// None
public var date: NaiveDate?
/// None
Expand All @@ -86,7 +86,7 @@ extension Paths {
/// None
public var callback: String?

public init(integer: Int? = nil, int32: Double? = nil, int64: Int? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: String, binary: String? = nil, date: NaiveDate? = nil, dateTime: AnyJSON? = nil, password: String? = nil, callback: String? = nil) {
public init(integer: Int? = nil, int32: Double? = nil, int64: Int? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: String, binary: Data? = nil, date: NaiveDate? = nil, dateTime: AnyJSON? = nil, password: String? = nil, callback: String? = nil) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -13,13 +13,13 @@ public struct FormatTest: Codable {
public var double: Double?
public var string: String?
public var byte: Data
public var binary: String?
public var binary: Data?
public var date: NaiveDate
public var dateTime: Date?
public var uuid: UUID?
public var password: String

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: String? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: Data? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand All @@ -45,7 +45,7 @@ public struct FormatTest: Codable {
self.double = try values.decodeIfPresent(Double.self, forKey: "double")
self.string = try values.decodeIfPresent(String.self, forKey: "string")
self.byte = try values.decode(Data.self, forKey: "byte")
self.binary = try values.decodeIfPresent(String.self, forKey: "binary")
self.binary = try values.decodeIfPresent(Data.self, forKey: "binary")
self.date = try values.decode(NaiveDate.self, forKey: "date")
self.dateTime = try values.decodeIfPresent(Date.self, forKey: "dateTime")
self.uuid = try values.decodeIfPresent(UUID.self, forKey: "uuid")
Expand Down
Expand Up @@ -77,7 +77,7 @@ extension Paths {
/// None
public var byte: Data
/// None
public var binary: String?
public var binary: Data?
/// None
public var date: NaiveDate?
/// None
Expand All @@ -87,7 +87,7 @@ extension Paths {
/// None
public var callback: String?

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: String? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: Data? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -13,13 +13,13 @@ public struct FormatTest: Codable {
public var double: Double?
public var string: String?
public var byte: Data
public var binary: String?
public var binary: Data?
public var date: NaiveDate
public var dateTime: Date?
public var uuid: UUID?
public var password: String

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: String? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: Data? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand All @@ -45,7 +45,7 @@ public struct FormatTest: Codable {
self.double = try values.decodeIfPresent(Double.self, forKey: "double")
self.string = try values.decodeIfPresent(String.self, forKey: "string")
self.byte = try values.decode(Data.self, forKey: "byte")
self.binary = try values.decodeIfPresent(String.self, forKey: "binary")
self.binary = try values.decodeIfPresent(Data.self, forKey: "binary")
self.date = try values.decode(NaiveDate.self, forKey: "date")
self.dateTime = try values.decodeIfPresent(Date.self, forKey: "dateTime")
self.uuid = try values.decodeIfPresent(UUID.self, forKey: "uuid")
Expand Down
Expand Up @@ -77,7 +77,7 @@ extension Paths {
/// None
public var byte: Data
/// None
public var binary: String?
public var binary: Data?
/// None
public var date: NaiveDate?
/// None
Expand All @@ -87,7 +87,7 @@ extension Paths {
/// None
public var callback: String?

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: String? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double, string: String? = nil, patternWithoutDelimiter: String, byte: Data, binary: Data? = nil, date: NaiveDate? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand Down
Expand Up @@ -13,13 +13,13 @@ public struct FormatTest: Codable {
public var double: Double?
public var string: String?
public var byte: Data
public var binary: String?
public var binary: Data?
public var date: NaiveDate
public var dateTime: Date?
public var uuid: UUID?
public var password: String

public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: String? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
public init(integer: Int? = nil, int32: Int32? = nil, int64: Int64? = nil, number: Double, float: Float? = nil, double: Double? = nil, string: String? = nil, byte: Data, binary: Data? = nil, date: NaiveDate, dateTime: Date? = nil, uuid: UUID? = nil, password: String) {
self.integer = integer
self.int32 = int32
self.int64 = int64
Expand All @@ -45,7 +45,7 @@ public struct FormatTest: Codable {
self.double = try values.decodeIfPresent(Double.self, forKey: "double")
self.string = try values.decodeIfPresent(String.self, forKey: "string")
self.byte = try values.decode(Data.self, forKey: "byte")
self.binary = try values.decodeIfPresent(String.self, forKey: "binary")
self.binary = try values.decodeIfPresent(Data.self, forKey: "binary")
self.date = try values.decode(NaiveDate.self, forKey: "date")
self.dateTime = try values.decodeIfPresent(Date.self, forKey: "dateTime")
self.uuid = try values.decodeIfPresent(UUID.self, forKey: "uuid")
Expand Down

0 comments on commit da8730b

Please sign in to comment.