Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions bin/configs/swift5-vapor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
generatorName: swift5
outputDir: samples/client/petstore/swift5/vaporLibrary
library: vapor
inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/swift5
generateAliasAsModel: true
additionalProperties:
projectName: PetstoreClient
useSPMFileStructure: true
useClasses: true
useBacktickEscapes: true
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
public static final String LENIENT_TYPE_CAST = "lenientTypeCast";
public static final String USE_SPM_FILE_STRUCTURE = "useSPMFileStructure";
public static final String SWIFT_PACKAGE_PATH = "swiftPackagePath";
public static final String USE_CLASSES = "useClasses";
public static final String USE_BACKTICK_ESCAPES = "useBacktickEscapes";
public static final String GENERATE_MODEL_ADDITIONAL_PROPERTIES = "generateModelAdditionalProperties";
public static final String HASHABLE_MODELS = "hashableModels";
protected static final String LIBRARY_ALAMOFIRE = "alamofire";
protected static final String LIBRARY_URLSESSION = "urlsession";
protected static final String LIBRARY_VAPOR = "vapor";
protected static final String RESPONSE_LIBRARY_PROMISE_KIT = "PromiseKit";
protected static final String RESPONSE_LIBRARY_RX_SWIFT = "RxSwift";
protected static final String RESPONSE_LIBRARY_RESULT = "Result";
Expand All @@ -81,6 +83,7 @@ public class Swift5ClientCodegen extends DefaultCodegen implements CodegenConfig
protected boolean swiftUseApiNamespace = false;
protected boolean useSPMFileStructure = false;
protected String swiftPackagePath = "Classes" + File.separator + "OpenAPIs";
protected boolean useClasses = false;
protected boolean useBacktickEscapes = false;
protected boolean generateModelAdditionalProperties = true;
protected boolean hashableModels = true;
Expand Down Expand Up @@ -120,7 +123,6 @@ public Swift5ClientCodegen() {
"Bool",
"Void",
"String",
"URL",
"Data",
"Date",
"Character",
Expand Down Expand Up @@ -224,8 +226,8 @@ public Swift5ClientCodegen() {
typeMapping.put("float", "Float");
typeMapping.put("number", "Double");
typeMapping.put("double", "Double");
typeMapping.put("file", "URL");
typeMapping.put("binary", "URL");
typeMapping.put("file", "Data");
typeMapping.put("binary", "Data");
typeMapping.put("ByteArray", "Data");
typeMapping.put("UUID", "UUID");
typeMapping.put("URI", "String");
Expand Down Expand Up @@ -281,13 +283,16 @@ public Swift5ClientCodegen() {
+ " and set the source path to Sources" + File.separator + "{{projectName}} (default: false)."));
cliOptions.add(new CliOption(SWIFT_PACKAGE_PATH, "Set a custom source path instead of "
+ projectName + File.separator + "Classes" + File.separator + "OpenAPIs" + "."));
cliOptions.add(new CliOption(USE_CLASSES, "Use final classes for models instead of structs (default: false)")
.defaultValue(Boolean.FALSE.toString()));

cliOptions.add(new CliOption(HASHABLE_MODELS,
"Make hashable models (default: true)")
.defaultValue(Boolean.TRUE.toString()));

supportedLibraries.put(LIBRARY_URLSESSION, "[DEFAULT] HTTP client: URLSession");
supportedLibraries.put(LIBRARY_ALAMOFIRE, "HTTP client: Alamofire");
supportedLibraries.put(LIBRARY_VAPOR, "HTTP client: Vapor");

CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "Library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
Expand Down Expand Up @@ -437,7 +442,7 @@ public void processOpts() {
additionalProperties.put(READONLY_PROPERTIES, readonlyProperties);

// Setup swiftUseApiNamespace option, which makes all the API
// classes inner-class of {{projectName}}API
// classes inner-class of {{projectName}}
if (additionalProperties.containsKey(SWIFT_USE_API_NAMESPACE)) {
setSwiftUseApiNamespace(convertPropertyToBooleanAndWriteBack(SWIFT_USE_API_NAMESPACE));
}
Expand Down Expand Up @@ -470,63 +475,69 @@ public void processOpts() {
}
additionalProperties.put(HASHABLE_MODELS, hashableModels);

if (additionalProperties.containsKey(USE_CLASSES)) {
setUseClasses(convertPropertyToBooleanAndWriteBack(USE_CLASSES));
}

setLenientTypeCast(convertPropertyToBooleanAndWriteBack(LENIENT_TYPE_CAST));

// make api and model doc path available in mustache template
additionalProperties.put("apiDocPath", apiDocPath);
additionalProperties.put("modelDocPath", modelDocPath);

supportingFiles.add(new SupportingFile("Podspec.mustache",
"",
projectName + ".podspec"));
supportingFiles.add(new SupportingFile("Cartfile.mustache",
"",
"Cartfile"));
if (!getLibrary().equals(LIBRARY_VAPOR)) {
supportingFiles.add(new SupportingFile("Podspec.mustache",
"",
projectName + ".podspec"));
supportingFiles.add(new SupportingFile("Cartfile.mustache",
"",
"Cartfile"));
supportingFiles.add(new SupportingFile("CodableHelper.mustache",
sourceFolder,
"CodableHelper.swift"));
supportingFiles.add(new SupportingFile("OpenISO8601DateFormatter.mustache",
sourceFolder,
"OpenISO8601DateFormatter.swift"));
supportingFiles.add(new SupportingFile("JSONDataEncoding.mustache",
sourceFolder,
"JSONDataEncoding.swift"));
supportingFiles.add(new SupportingFile("JSONEncodingHelper.mustache",
sourceFolder,
"JSONEncodingHelper.swift"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache",
"",
"git_push.sh"));
supportingFiles.add(new SupportingFile("SynchronizedDictionary.mustache",
sourceFolder,
"SynchronizedDictionary.swift"));
supportingFiles.add(new SupportingFile("XcodeGen.mustache",
"",
"project.yml"));
supportingFiles.add(new SupportingFile("APIHelper.mustache",
sourceFolder,
"APIHelper.swift"));
supportingFiles.add(new SupportingFile("Models.mustache",
sourceFolder,
"Models.swift"));
}
supportingFiles.add(new SupportingFile("Package.swift.mustache",
"",
"Package.swift"));
supportingFiles.add(new SupportingFile("APIHelper.mustache",
sourceFolder,
"APIHelper.swift"));
supportingFiles.add(new SupportingFile("Configuration.mustache",
sourceFolder,
"Configuration.swift"));
supportingFiles.add(new SupportingFile("Extensions.mustache",
sourceFolder,
"Extensions.swift"));
supportingFiles.add(new SupportingFile("Models.mustache",
sourceFolder,
"Models.swift"));
supportingFiles.add(new SupportingFile("APIs.mustache",
sourceFolder,
"APIs.swift"));
supportingFiles.add(new SupportingFile("CodableHelper.mustache",
sourceFolder,
"CodableHelper.swift"));
supportingFiles.add(new SupportingFile("OpenISO8601DateFormatter.mustache",
sourceFolder,
"OpenISO8601DateFormatter.swift"));
supportingFiles.add(new SupportingFile("JSONDataEncoding.mustache",
sourceFolder,
"JSONDataEncoding.swift"));
supportingFiles.add(new SupportingFile("JSONEncodingHelper.mustache",
sourceFolder,
"JSONEncodingHelper.swift"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache",
"",
"git_push.sh"));
supportingFiles.add(new SupportingFile("SynchronizedDictionary.mustache",
sourceFolder,
"SynchronizedDictionary.swift"));
supportingFiles.add(new SupportingFile("gitignore.mustache",
"",
".gitignore"));
supportingFiles.add(new SupportingFile("README.mustache",
"",
"README.md"));
supportingFiles.add(new SupportingFile("XcodeGen.mustache",
"",
"project.yml"));

switch (getLibrary()) {
case LIBRARY_ALAMOFIRE:
Expand All @@ -541,6 +552,9 @@ public void processOpts() {
sourceFolder,
"URLSessionImplementations.swift"));
break;
case LIBRARY_VAPOR:
additionalProperties.put("useVapor", true);
break;
default:
break;
}
Expand Down Expand Up @@ -602,7 +616,7 @@ public String getSchemaType(Schema p) {

@Override
public boolean isDataTypeFile(String dataType) {
return "URL".equals(dataType);
return "Data".equals(dataType);
}

@Override
Expand Down Expand Up @@ -882,6 +896,10 @@ public void setSwiftPackagePath(String swiftPackagePath) {
this.swiftPackagePath = swiftPackagePath;
}

public void setUseClasses(boolean useClasses) {
this.useClasses = useClasses;
}

public void setUseBacktickEscapes(boolean useBacktickEscapes) {
this.useBacktickEscapes = useBacktickEscapes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
// https://openapi-generator.tech
//

import Foundation
import Foundation{{#useVapor}}
import Vapor{{/useVapor}}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} struct APIHelper {
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static func rejectNil(_ source: [String: Any?]) -> [String: Any]? {
Expand Down
23 changes: 16 additions & 7 deletions modules/openapi-generator/src/main/resources/swift5/APIs.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@
//

import Foundation
{{#useVapor}}
import Vapor
{{/useVapor}}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class {{projectName}}API {
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class {{projectName}} {
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var basePath = "{{{basePath}}}"
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var credential: URLCredential?
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var customHeaders: [String: String] = [:]{{#useAlamofire}}
{{#useVapor}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var customHeaders: HTTPHeaders = [:]
{{/useVapor}}
{{^useVapor}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var customHeaders: [String: String] = [:]
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var credential: URLCredential?{{#useAlamofire}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory(){{/useAlamofire}}{{#useURLSession}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var requestBuilderFactory: RequestBuilderFactory = URLSessionRequestBuilderFactory(){{/useURLSession}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var apiResponseQueue: DispatchQueue = .main
{{/useVapor}}
}

{{#useVapor}}{{/useVapor}}{{^useVapor}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class RequestBuilder<T> {
var credential: URLCredential?
var headers: [String: String]
Expand All @@ -33,7 +42,7 @@ import Foundation
self.parameters = parameters
self.headers = headers

addHeaders({{projectName}}API.customHeaders)
addHeaders({{projectName}}.customHeaders)
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func addHeaders(_ aHeaders: [String: String]) {
Expand All @@ -42,7 +51,7 @@ import Foundation
}
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}API.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, Error>) -> Void) { }
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func execute(_ apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue, _ completion: @escaping (_ result: Swift.Result<Response<T>, Error>) -> Void) { }

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func addHeader(name: String, value: String) -> Self {
if !value.isEmpty {
Expand All @@ -52,12 +61,12 @@ import Foundation
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} func addCredential() -> Self {
credential = {{projectName}}API.credential
credential = {{projectName}}.credential
return self
}
}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} protocol RequestBuilderFactory {
func getNonDecodableBuilder<T>() -> RequestBuilder<T>.Type
func getBuilder<T: Decodable>() -> RequestBuilder<T>.Type
}
}{{/useVapor}}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
//

import Foundation
{{#useVapor}}import Vapor{{/useVapor}}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class Configuration {
{{#useVapor}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var apiClient: Vapor.Client? = nil
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var apiWrapper: (inout Vapor.ClientRequest) throws -> () = { _ in }
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var contentConfiguration = ContentConfiguration.default(){{/useVapor}}{{^useVapor}}
// This value is used to configure the date formatter that is used to serialize dates into JSON format.
// You must set it prior to encoding any dates, and it will only be read once.
@available(*, unavailable, message: "To set a different date format, use CodableHelper.dateFormatter instead.")
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"{{/useVapor}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import Foundation
#if canImport(AnyCodable)
import AnyCodable
#endif{{#usePromiseKit}}
import PromiseKit{{/usePromiseKit}}
import PromiseKit{{/usePromiseKit}}{{#useVapor}}
import Vapor{{/useVapor}}

{{^useVapor}}
extension Bool: JSONEncodable {
func encodeToJSON() -> Any { return self as Any }
}
Expand Down Expand Up @@ -94,7 +96,7 @@ extension UUID: JSONEncodable {
func encodeToJSON() -> Any {
return self.uuidString
}
}{{#generateModelAdditionalProperties}}
}{{/useVapor}}{{#generateModelAdditionalProperties}}

extension String: CodingKey {

Expand Down Expand Up @@ -180,13 +182,13 @@ extension KeyedDecodingContainerProtocol {
return map
}

}{{/generateModelAdditionalProperties}}
}{{/generateModelAdditionalProperties}}{{^useVapor}}

extension HTTPURLResponse {
var isStatusCodeSuccessful: Bool {
return Array(200 ..< 300).contains(statusCode)
}
}{{#usePromiseKit}}
}{{/useVapor}}{{#usePromiseKit}}

extension RequestBuilder {
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func execute() -> Promise<Response<T>> {
Expand All @@ -202,6 +204,41 @@ extension RequestBuilder {
return deferred.promise
}
}{{/usePromiseKit}}
{{#useVapor}}

extension UUID: Content { }

extension URL: Content { }

extension Bool: Content { }

extension Set: ResponseEncodable where Element: Content {
public func encodeResponse(for request: Vapor.Request) -> EventLoopFuture<Vapor.Response> {
let response = Vapor.Response()
do {
try response.content.encode(Array(self))
} catch {
return request.eventLoop.makeFailedFuture(error)
}
return request.eventLoop.makeSucceededFuture(response)
}
}

extension Set: RequestDecodable where Element: Content {
public static func decodeRequest(_ request: Vapor.Request) -> EventLoopFuture<Self> {
do {
let content = try request.content.decode([Element].self)
return request.eventLoop.makeSucceededFuture(Set(content))
} catch {
return request.eventLoop.makeFailedFuture(error)
}
}
}

extension Set: Content where Element: Content { }

extension AnyCodable: Content {}
{{/useVapor}}

#if canImport(AnyCodable)
extension AnyCodable: Hashable {
Expand Down
Loading