diff --git a/boat-scaffold/src/main/java/org/openapitools/codegen/languages/BoatSwift5Codegen.java b/boat-scaffold/src/main/java/org/openapitools/codegen/languages/BoatSwift5Codegen.java index 25c3749ee..a545bf994 100644 --- a/boat-scaffold/src/main/java/org/openapitools/codegen/languages/BoatSwift5Codegen.java +++ b/boat-scaffold/src/main/java/org/openapitools/codegen/languages/BoatSwift5Codegen.java @@ -68,6 +68,7 @@ public void processOpts() { super.processOpts(); additionalProperties.put("useDBSDataProvider", getLibrary().equals(LIBRARY_DBS)); supportingFiles.add(new SupportingFile("AnyCodable.swift.mustache", sourceFolder, "AnyCodable.swift")); + supportingFiles.add(new SupportingFile("CommonClient+ExplodedParameters.swift.mustache", sourceFolder, "CommonClient+ExplodedParameters.swift")); if (additionalProperties.containsKey(DEPENDENCY_MANAGEMENT)) { Object dependenciesAsObject = additionalProperties.get(DEPENDENCY_MANAGEMENT); diff --git a/boat-scaffold/src/main/templates/boat-swift5/CommonClient+ExplodedParameters.swift.mustache b/boat-scaffold/src/main/templates/boat-swift5/CommonClient+ExplodedParameters.swift.mustache new file mode 100644 index 000000000..2178a3bca --- /dev/null +++ b/boat-scaffold/src/main/templates/boat-swift5/CommonClient+ExplodedParameters.swift.mustache @@ -0,0 +1,137 @@ +// +// CommonClient+ExplodedParameters.swift +// +// Generated by openapi-generator +// https://openapi-generator.tech +// + + +import Foundation +import Backbase +import ClientCommonGen2 + +/// ArrayParam wrapper for controlling OpenAPI array parameter encoding +public struct ArrayParam { + public init(_ value: [Any?], exploded: Bool) { + self.value = value + self.explode = exploded + } + + public let value: [Any?] + public let explode: Bool + + /// Returns URLQueryItems based on explode setting + func queryItems(for key: String) -> [URLQueryItem] { + if explode { + return value.compactMap { $0 }.map { URLQueryItem(name: key, value: "\($0)") } + } else { + let joined = value.compactMap { $0 }.map { "\($0)" }.joined(separator: ",") + return [URLQueryItem(name: key, value: joined)] + } + } +} + +// MARK: - URLEncoding Extension with exploded arguments support +public extension URLEncoding { + /// Creates a URL request by encoding parameters with exploded array support + /// and applying them onto an existing request url. + /// + /// This method supports the ArrayParam type for controlling exploded vs comma-separated arrays + /// according to OpenAPI specification. + /// + /// - parameter urlRequest: The request to have parameters applied. + /// - parameter parameters: The parameters to apply. Can include ArrayParam instances. + /// + /// - throws: An `Error` if the encoding process encounters an error. + /// + /// - returns: The encoded request. + static func encodeWithExplodedSupport(_ urlRequest: URLRequest, with parameters: [String: Any]?) throws -> URLRequest { + var urlRequest = urlRequest + guard let parameters = parameters else { return urlRequest } + + guard let url = urlRequest.url else { + throw CallError.requestMissingUrl + } + + if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty { + urlComponents.queryItems = mapValuesToQueryItemsWithExplodedSupport(parameters) + urlRequest.url = urlComponents.url + } + + return urlRequest + } + + private static func mapValuesToQueryItemsWithExplodedSupport(_ source: [String: Any?]) -> [URLQueryItem]? { + let items = source.compactMap { key, value -> [URLQueryItem]? in + guard let value else { + return nil + } + + switch value { + case let array as [Any?]: + return ArrayParam(array, exploded: true) + .queryItems(for: key) + + case let arrayParam as ArrayParam: + return arrayParam + .queryItems(for: key) + + default: + return [URLQueryItem(name: key, value: "\(value)")] + } + }.flatMap { $0 } + + return items.isEmpty ? nil : items + } +} + +// Helper extensions for easier ArrayParam creation +public extension Array { + /// Creates an ArrayParam with exploded=true for OpenAPI query parameter encoding + /// Example: ["a", "b"].exploded -> ?param=a¶m=b + var exploded: ArrayParam { + ArrayParam(self, exploded: true) + } + + /// Creates an ArrayParam with exploded=false for OpenAPI query parameter encoding + /// Example: ["a", "b"].commaSeparated -> ?param=a,b + var commaSeparated: ArrayParam { + ArrayParam(self, exploded: false) + } +} + +// MARK: - RequestBuilder Extension with Custom Encoding +public extension RequestBuilder { + + /// createURLRequest with exploded support + /// - Parameters: + /// - requestUrl: request URL + /// - method: method + /// - queryParameters: query parameters + /// - bodyParameters: body parameters + /// - bodyType: BodyType + /// - headers: an object of type [String: String] + /// - Returns: URLRequest with exploded parameter support + static func createURLRequestWithExplodedSupport(requestUrl: URL, + method: String, + queryParameters: [String: Any]?, + bodyParameters: [String: Any]?, + bodyType: BodyType, + headers: [String: String] = [:]) throws -> URLRequest { + var modifiedRequest = try URLEncoding.encodeWithExplodedSupport(.init(url: requestUrl), with: queryParameters) + modifiedRequest.httpMethod = method + headers.forEach { modifiedRequest.setValue($0.value, forHTTPHeaderField: $0.key) } + Backbase.authClient().tokens().forEach { modifiedRequest.setValue($0.value, forHTTPHeaderField: $0.key) } + + switch bodyType { + case .none: + break + case .json: + modifiedRequest = JSONDataEncoding.encode(modifiedRequest, with: bodyParameters) + case .multipartForm: + modifiedRequest = try FileUploadEncoding.encode(modifiedRequest, with: bodyParameters) + } + + return modifiedRequest + } +} diff --git a/boat-scaffold/src/main/templates/boat-swift5/_param.mustache b/boat-scaffold/src/main/templates/boat-swift5/_param.mustache index dc5730bf8..8a44490ef 100644 --- a/boat-scaffold/src/main/templates/boat-swift5/_param.mustache +++ b/boat-scaffold/src/main/templates/boat-swift5/_param.mustache @@ -1 +1 @@ -"{{baseName}}": {{#isFreeFormObject}}(params.{{paramName}} as{{^required}}?{{/required}} String){{^required}}?{{/required}}.encodeToJSON(){{/isFreeFormObject}}{{^isFreeFormObject}}params.{{paramName}}{{^required}}?{{/required}}.{{#isDateTime}}encodeToJSONDateTime{{/isDateTime}}{{^isDateTime}}encodeToJSON{{/isDateTime}}(){{/isFreeFormObject}} \ No newline at end of file +"{{baseName}}": {{#isContainer}}params.{{paramName}}.{{#isExplode}}exploded{{/isExplode}}{{^isExplode}}commaSeparated{{/isExplode}}{{/isContainer}}{{^isContainer}}{{#isFreeFormObject}}(params.{{paramName}} as{{^required}}?{{/required}} String){{^required}}?{{/required}}.encodeToJSON(){{/isFreeFormObject}}{{^isFreeFormObject}}{{#isEnum}}params.{{paramName}}{{^required}}?{{/required}}.rawValue{{/isEnum}}{{^isEnum}}params.{{paramName}}{{^required}}?{{/required}}.{{#isDateTime}}encodeToJSONDateTime{{/isDateTime}}{{^isDateTime}}encodeToJSON{{/isDateTime}}(){{/isEnum}}{{/isFreeFormObject}}{{/isContainer}} \ No newline at end of file diff --git a/boat-scaffold/src/main/templates/boat-swift5/api.mustache b/boat-scaffold/src/main/templates/boat-swift5/api.mustache index 52381839a..110adf954 100644 --- a/boat-scaffold/src/main/templates/boat-swift5/api.mustache +++ b/boat-scaffold/src/main/templates/boat-swift5/api.mustache @@ -273,7 +273,7 @@ extension {{moduleName}}Client { headers: headerParameters{{/-first}}{{/headerParams}}) {{/useMsdkSwift}} {{^useMsdkSwift}} - let request = try ClientCommonGen2.RequestBuilder.createURLRequest(requestUrl: url, + let request = try ClientCommonGen2.RequestBuilder.createURLRequestWithExplodedSupport(requestUrl: url, method: "{{httpMethod}}", queryParameters: {{#hasQueryParams}}queryParameters.compactMapValues({ $0 }){{/hasQueryParams}}{{^hasQueryParams}}nil{{/hasQueryParams}}, bodyParameters: parameters,