From 8067612e06172942c5d661f0988bb866492d1cab Mon Sep 17 00:00:00 2001 From: ehyche Date: Mon, 11 Sep 2017 23:37:39 -0700 Subject: [PATCH] Handle when response is a file URL. (#6469) This is the equivalent change in the swift4 module which was made in the swift3 module in this PR: https://github.com/swagger-api/swagger-codegen/pull/6274 This updates AlamofireImplementations.mustache to handle when the response is an URL. It also makes changes in the generated sample code for: * default configuration (no promisekit or rxswift) * promisekit * rxswift Also, in order to build, the generated code needed to be updated with the change in CodableHelper which changes dataDecodingStrategy to ".base64" from its previous definition in earlier Xcode 9 betas. * --- .../swift4/AlamofireImplementations.mustache | 110 ++++++++++++++++++ .../Swaggers/AlamofireImplementations.swift | 110 ++++++++++++++++++ .../Classes/Swaggers/CodableHelper.swift | 4 +- .../Swaggers/AlamofireImplementations.swift | 110 ++++++++++++++++++ .../Classes/Swaggers/CodableHelper.swift | 4 +- .../Swaggers/AlamofireImplementations.swift | 110 ++++++++++++++++++ .../Classes/Swaggers/CodableHelper.swift | 4 +- 7 files changed, 446 insertions(+), 6 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache index b9d2a2727de..0d294aee4ce 100644 --- a/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift4/AlamofireImplementations.mustache @@ -142,6 +142,56 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.Error(400, dataResponse.data, requestParserError)) + } catch let error { + completion(nil, ErrorResponse.Error(400, dataResponse.data, error)) + } + return + }) case is Void.Type: validatedRequest.responseData(completionHandler: { (voidResponse) in cleanupRequest() @@ -191,6 +241,66 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } + +} + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL } public enum AlamofireDecodableRequestBuilderError: Error { diff --git a/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift b/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift index b9d2a2727de..0d294aee4ce 100644 --- a/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift +++ b/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift @@ -142,6 +142,56 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.Error(400, dataResponse.data, requestParserError)) + } catch let error { + completion(nil, ErrorResponse.Error(400, dataResponse.data, error)) + } + return + }) case is Void.Type: validatedRequest.responseData(completionHandler: { (voidResponse) in cleanupRequest() @@ -191,6 +241,66 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } + +} + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL } public enum AlamofireDecodableRequestBuilderError: Error { diff --git a/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/CodableHelper.swift b/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/CodableHelper.swift index 4ae4ca267ac..d56cb0e7348 100644 --- a/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/CodableHelper.swift +++ b/samples/client/petstore/swift4/default/PetstoreClient/Classes/Swaggers/CodableHelper.swift @@ -16,7 +16,7 @@ open class CodableHelper { var returnedError: Error? = nil let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .base64Decode + decoder.dataDecodingStrategy = .base64 if #available(iOS 10.0, *) { decoder.dateDecodingStrategy = .iso8601 } @@ -38,7 +38,7 @@ open class CodableHelper { if prettyPrint { encoder.outputFormatting = .prettyPrinted } - encoder.dataEncodingStrategy = .base64Encode + encoder.dataEncodingStrategy = .base64 if #available(iOS 10.0, *) { encoder.dateEncodingStrategy = .iso8601 } diff --git a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift index b9d2a2727de..0d294aee4ce 100644 --- a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift +++ b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift @@ -142,6 +142,56 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.Error(400, dataResponse.data, requestParserError)) + } catch let error { + completion(nil, ErrorResponse.Error(400, dataResponse.data, error)) + } + return + }) case is Void.Type: validatedRequest.responseData(completionHandler: { (voidResponse) in cleanupRequest() @@ -191,6 +241,66 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } + +} + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL } public enum AlamofireDecodableRequestBuilderError: Error { diff --git a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/CodableHelper.swift b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/CodableHelper.swift index 4ae4ca267ac..d56cb0e7348 100644 --- a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/CodableHelper.swift +++ b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/Swaggers/CodableHelper.swift @@ -16,7 +16,7 @@ open class CodableHelper { var returnedError: Error? = nil let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .base64Decode + decoder.dataDecodingStrategy = .base64 if #available(iOS 10.0, *) { decoder.dateDecodingStrategy = .iso8601 } @@ -38,7 +38,7 @@ open class CodableHelper { if prettyPrint { encoder.outputFormatting = .prettyPrinted } - encoder.dataEncodingStrategy = .base64Encode + encoder.dataEncodingStrategy = .base64 if #available(iOS 10.0, *) { encoder.dateEncodingStrategy = .iso8601 } diff --git a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift index b9d2a2727de..0d294aee4ce 100644 --- a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift +++ b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/AlamofireImplementations.swift @@ -142,6 +142,56 @@ open class AlamofireRequestBuilder: RequestBuilder { nil ) }) + case is URL.Type: + validatedRequest.responseData(completionHandler: { (dataResponse) in + cleanupRequest() + + do { + + guard !dataResponse.result.isFailure else { + throw DownloadException.responseFailed + } + + guard let data = dataResponse.data else { + throw DownloadException.responseDataMissing + } + + guard let request = request.request else { + throw DownloadException.requestMissing + } + + let fileManager = FileManager.default + let urlRequest = try request.asURLRequest() + let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] + let requestURL = try self.getURL(from: urlRequest) + + var requestPath = try self.getPath(from: requestURL) + + if let headerFileName = self.getFileName(fromContentDisposition: dataResponse.response?.allHeaderFields["Content-Disposition"] as? String) { + requestPath = requestPath.appending("/\(headerFileName)") + } + + let filePath = documentsDirectory.appendingPathComponent(requestPath) + let directoryPath = filePath.deletingLastPathComponent().path + + try fileManager.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil) + try data.write(to: filePath, options: .atomic) + + completion( + Response( + response: dataResponse.response!, + body: (filePath as! T) + ), + nil + ) + + } catch let requestParserError as DownloadException { + completion(nil, ErrorResponse.Error(400, dataResponse.data, requestParserError)) + } catch let error { + completion(nil, ErrorResponse.Error(400, dataResponse.data, error)) + } + return + }) case is Void.Type: validatedRequest.responseData(completionHandler: { (voidResponse) in cleanupRequest() @@ -191,6 +241,66 @@ open class AlamofireRequestBuilder: RequestBuilder { } return httpHeaders } + + fileprivate func getFileName(fromContentDisposition contentDisposition : String?) -> String? { + + guard let contentDisposition = contentDisposition else { + return nil + } + + let items = contentDisposition.components(separatedBy: ";") + + var filename : String? = nil + + for contentItem in items { + + let filenameKey = "filename=" + guard let range = contentItem.range(of: filenameKey) else { + break + } + + filename = contentItem + return filename? + .replacingCharacters(in: range, with:"") + .replacingOccurrences(of: "\"", with: "") + .trimmingCharacters(in: .whitespacesAndNewlines) + } + + return filename + + } + + fileprivate func getPath(from url : URL) throws -> String { + + guard var path = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.path else { + throw DownloadException.requestMissingPath + } + + if path.hasPrefix("/") { + path.remove(at: path.startIndex) + } + + return path + + } + + fileprivate func getURL(from urlRequest : URLRequest) throws -> URL { + + guard let url = urlRequest.url else { + throw DownloadException.requestMissingURL + } + + return url + } + +} + +fileprivate enum DownloadException : Error { + case responseDataMissing + case responseFailed + case requestMissing + case requestMissingPath + case requestMissingURL } public enum AlamofireDecodableRequestBuilderError: Error { diff --git a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/CodableHelper.swift b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/CodableHelper.swift index 4ae4ca267ac..d56cb0e7348 100644 --- a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/CodableHelper.swift +++ b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/Swaggers/CodableHelper.swift @@ -16,7 +16,7 @@ open class CodableHelper { var returnedError: Error? = nil let decoder = JSONDecoder() - decoder.dataDecodingStrategy = .base64Decode + decoder.dataDecodingStrategy = .base64 if #available(iOS 10.0, *) { decoder.dateDecodingStrategy = .iso8601 } @@ -38,7 +38,7 @@ open class CodableHelper { if prettyPrint { encoder.outputFormatting = .prettyPrinted } - encoder.dataEncodingStrategy = .base64Encode + encoder.dataEncodingStrategy = .base64 if #available(iOS 10.0, *) { encoder.dateEncodingStrategy = .iso8601 }