Skip to content

Commit

Permalink
feat(swift5): add support for rate limit
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibaultBee committed Apr 23, 2024
1 parent f83f015 commit 2f281d0
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 43 deletions.
13 changes: 4 additions & 9 deletions templates/swift5/Upload/RequestTaskQueue.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class RequestTaskQueue<T>: RequestTask {

internal func execute(_ requestBuilder: RequestBuilder<T>,
apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue,
completion: @escaping (_ data: T?, _ error: Error?) -> Void) -> Void {
completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) -> Void {
requestBuilders.append(requestBuilder)
return operationQueue.addOperation(RequestOperation(requestBuilder: requestBuilder, apiResponseQueue: apiResponseQueue, willExecuteRequestBuilder: willExecuteRequestBuilder, completion: completion))
}
Expand All @@ -67,11 +67,11 @@ public class RequestTaskQueue<T>: RequestTask {
final class RequestOperation<T>: Operation {
private let requestBuilder: RequestBuilder<T>
private let apiResponseQueue: DispatchQueue
private let completion: (_ data: T?, _ error: Error?) -> Void
private let completion: (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void
private let willExecuteRequestBuilder: (_: RequestBuilder<T>) -> Void
private let group = DispatchGroup()
init(requestBuilder: RequestBuilder<T>, apiResponseQueue: DispatchQueue, willExecuteRequestBuilder: @escaping (_: RequestBuilder<T>) -> Void, completion: @escaping (_ data: T?, _ error: Error?) -> Void) {
init(requestBuilder: RequestBuilder<T>, apiResponseQueue: DispatchQueue, willExecuteRequestBuilder: @escaping (_: RequestBuilder<T>) -> Void, completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void) {
self.requestBuilder = requestBuilder
self.apiResponseQueue = apiResponseQueue
self.willExecuteRequestBuilder = willExecuteRequestBuilder
Expand All @@ -87,12 +87,7 @@ final class RequestOperation<T>: Operation {

self.willExecuteRequestBuilder(requestBuilder)
requestBuilder.execute(apiResponseQueue) { result in
switch result {
case let .success(response):
self.completion(response.body, nil)
case let .failure(error):
self.completion(nil, error)
}
self.completion(result)
self.group.leave()
}
// Make task synchronous
Expand Down
29 changes: 15 additions & 14 deletions templates/swift5/Upload/UploadChunkRequestTaskQueue.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
private let onProgressReady: ((Progress) -> Void)?
private let apiResponseQueue: DispatchQueue
private let completion: (_ data: Video?, _ error: Error?) -> Void
private let completion: (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void
private var videoId: String?
private let _uploadProgress: Progress
Expand All @@ -19,7 +19,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
queueLabel: String,
onProgressReady: ((Progress) -> Void)? = nil,
apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue,
completion: @escaping (_ data: Video?, _ error: Error?) -> Void) {
completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) {
_uploadProgress = Progress(totalUnitCount: fileSize)
self.requestBuilders = requestBuilders
self.onProgressReady = onProgressReady
Expand All @@ -38,7 +38,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
file: URL,
onProgressReady: ((Progress) -> Void)? = nil,
apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue,
completion: @escaping (_ data: Video?, _ error: Error?) -> Void) throws {
completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) throws {
let chunkInputStreams = try FileChunkInputStreamsBuilder(file: file).build()
let numOfChunks = chunkInputStreams.count
Expand All @@ -56,7 +56,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
videoId: String? = nil,
onProgressReady: ((Progress) -> Void)? = nil,
apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue,
completion: @escaping (_ data: Video?, _ error: Error?) -> Void) throws {
completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) throws {
let chunkInputStreams = try FileChunkInputStreamsBuilder(file: file).build()
let numOfChunks = chunkInputStreams.count
Expand Down Expand Up @@ -87,21 +87,22 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
}
}

private func completionHook(_ data: Video?, _ error: Error?) -> Void {
if let error = error {
if !hasSentError {
hasSentError = true
cancel()
completion(nil, error)
}
} else {
private func completionHook(_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void {
switch result {
case let .success(response):
if (videoId == nil) {
videoId = data?.videoId
videoId = response.body.videoId
}
if (requestBuilders.allSatisfy {
$0.requestTask.state == .finished
}) {
completion(data, nil)
completion(result)
}
case .failure(_):
if !hasSentError {
hasSentError = true
cancel()
completion(result)
}
}
}
Expand Down
71 changes: 51 additions & 20 deletions templates/swift5/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,42 @@ extension {{projectName}}API {
{{/isDeprecated}}
@discardableResult
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}{{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{dataType}}? = nil, {{/vendorExtensions.x-client-copy-from-response}}{{#vendorExtensions.x-client-chunk-upload}}onProgressReady: ((Progress) -> Void)? = nil, {{/vendorExtensions.x-client-chunk-upload}}apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue, completion: @escaping ((_ data: {{{returnType}}}{{^returnType}}Void{{/returnType}}?, _ error: Error?) -> Void)){{#vendorExtensions.x-client-chunk-upload}} throws{{/vendorExtensions.x-client-chunk-upload}} -> RequestTask {
return {{#vendorExtensions.x-client-chunk-upload}}try {{/vendorExtensions.x-client-chunk-upload}}{{operationId}}({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}}, {{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{paramName}}, {{/vendorExtensions.x-client-copy-from-response}}onProgressReady: onProgressReady{{/vendorExtensions.x-client-chunk-upload}}, apiResponseQueue: apiResponseQueue) { result in
switch result {
{{#returnType}}
case let .success(response):
completion(response.body, nil)
{{/returnType}}
{{^returnType}}
case .success:
completion((), nil)
{{/returnType}}
case let .failure(error):
completion(nil, error)
}
}
}

/**
{{#summary}}
{{{.}}}
{{/summary}}{{#allParams}}
- parameter {{paramName}}: ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}}
- parameter onProgressReady: progress handler to receive request progress.{{/vendorExtensions.x-client-chunk-upload}}
- parameter apiResponseQueue: The queue on which api response is dispatched.
- parameter completion: completion handler to receive the result of the request (incl. headers).
*/
{{#isDeprecated}}
@available(*, deprecated, message: "This operation is deprecated.")
{{/isDeprecated}}
@discardableResult
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}{{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{dataType}}? = nil, {{/vendorExtensions.x-client-copy-from-response}}{{#vendorExtensions.x-client-chunk-upload}}onProgressReady: ((Progress) -> Void)? = nil, {{/vendorExtensions.x-client-chunk-upload}}apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<{{{returnType}}}{{^returnType}}Void{{/returnType}}>, ErrorResponse>) -> Void){{#vendorExtensions.x-client-chunk-upload}} throws{{/vendorExtensions.x-client-chunk-upload}} -> RequestTask {
{{#vendorExtensions.x-client-chunk-upload}}
if (try file.isMultiChunk) {
return try UploadChunkRequestTaskQueue({{#allParams}}{{^isFile}}{{paramName}}: {{paramName}}{{/isFile}}{{/allParams}}, file: file, {{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{paramName}}, {{/vendorExtensions.x-client-copy-from-response}}onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
} else {
{{/vendorExtensions.x-client-chunk-upload}}
return {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}}, {{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{paramName}}, {{/vendorExtensions.x-client-copy-from-response}}onProgressReady: onProgressReady{{/vendorExtensions.x-client-chunk-upload}}).execute(apiResponseQueue) { result in
switch result {
{{#returnType}}
case let .success(response):
completion(response.body, nil)
{{/returnType}}
{{^returnType}}
case .success:
completion((), nil)
{{/returnType}}
case let .failure(error):
completion(nil, error)
}
}
return {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-chunk-upload}}, {{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{paramName}}, {{/vendorExtensions.x-client-copy-from-response}}onProgressReady: onProgressReady{{/vendorExtensions.x-client-chunk-upload}}).execute(apiResponseQueue, completion)
{{#vendorExtensions.x-client-chunk-upload}}
}
{{/vendorExtensions.x-client-chunk-upload}}
Expand Down Expand Up @@ -134,18 +151,32 @@ extension {{projectName}}API {
}

public func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask {
let requestTask = uploadPart(file: file, partId: partId, isLastPart: isLastPart, onProgressReady: onProgressReady, completion: { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
})
return requestTask
}

public func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = {{projectName}}.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) -> RequestTask {
var numOfChunks: Int? = nil
if (isLastPart) {
numOfChunks = partId
}
let requestBuilder = {{operationId}}WithRequestBuilder({{#allParams}}{{^isFile}}{{paramName}}: {{paramName}}, {{/isFile}}{{/allParams}}file: file, {{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{paramName}}, {{/vendorExtensions.x-client-copy-from-response}}chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in{{#vendorExtensions.x-client-copy-from-response}}
if let data = data {
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { result in{{#vendorExtensions.x-client-copy-from-response}}
switch result {
case let .success(response):
if self.videoId == nil {
self.videoId = data.videoId
self.videoId = response.body.videoId
}
case .failure(_): break
}{{/vendorExtensions.x-client-copy-from-response}}
completion(data, error)
completion(result)
}
return requestBuilder.requestTask
}
Expand Down Expand Up @@ -495,7 +526,7 @@ extension {{projectName}}API {
{{#isDeprecated}}
@available(*, deprecated, message: "This operation is deprecated.")
{{/isDeprecated}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-copy-from-response}}, {{paramName}}: {{{dataType}}}? = nil{{/vendorExtensions.x-client-copy-from-response}}{{#vendorExtensions.x-client-chunk-upload}}, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil{{/vendorExtensions.x-client-chunk-upload}}) -> RequestBuilder<{{{returnType}}}{{^returnType}}Void{{/returnType}}> {
internal class func {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-client-copy-from-response}}, {{paramName}}: {{{dataType}}}? = nil{{/vendorExtensions.x-client-copy-from-response}}{{#vendorExtensions.x-client-chunk-upload}}, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil{{/vendorExtensions.x-client-chunk-upload}}) -> RequestBuilder<{{{returnType}}}{{^returnType}}Void{{/returnType}}> {
{{^pathParams}}let{{/pathParams}}{{#pathParams}}{{#-first}}var{{/-first}}{{/pathParams}} localVariablePath = "{{{path}}}"{{#pathParams}}
let {{paramName}}PreEscape = "\({{#isEnum}}{{paramName}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}.rawValue{{/isContainer}}{{/isEnum}}{{^isEnum}}APIHelper.mapValueToPathItem({{paramName}}){{/isEnum}})"
let {{paramName}}PostEscape = {{paramName}}PreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
Expand Down Expand Up @@ -574,7 +605,7 @@ extension {{projectName}}API {
{{#isDeprecated}}
@available(*, deprecated, message: "This operation is deprecated.")
{{/isDeprecated}}
open class func {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{#isFile}}FileChunkInputStream{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}}{{/isEnum}}{{^required}}? = nil{{/required}}, {{/allParams}}{{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{{dataType}}}? = nil, {{/vendorExtensions.x-client-copy-from-response}}chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<{{{returnType}}}{{^returnType}}Void{{/returnType}}> {
internal class func {{operationId}}WithRequestBuilder({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{datatypeWithEnum}}{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{#isFile}}FileChunkInputStream{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}}{{/isEnum}}{{^required}}? = nil{{/required}}, {{/allParams}}{{#vendorExtensions.x-client-copy-from-response}}{{paramName}}: {{{dataType}}}? = nil, {{/vendorExtensions.x-client-copy-from-response}}chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<{{{returnType}}}{{^returnType}}Void{{/returnType}}> {
{{^pathParams}}let{{/pathParams}}{{#pathParams}}{{#-first}}var{{/-first}}{{/pathParams}} localVariablePath = "{{{path}}}"{{#pathParams}}
let {{paramName}}PreEscape = "\({{#isEnum}}{{paramName}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}.rawValue{{/isContainer}}{{/isEnum}}{{^isEnum}}APIHelper.mapValueToPathItem({{paramName}}){{/isEnum}})"
let {{paramName}}PostEscape = {{paramName}}PreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
Expand Down
1 change: 1 addition & 0 deletions templates/swift5/api_doc.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Method | HTTP request | Description
{{^useRxSwift}}
{{^useVapor}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}completion: @escaping (_ data: {{{returnType}}}{{^returnType}}Void{{/returnType}}?, _ error: Error?) -> Void)
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func {{operationId}}({{#allParams}}{{paramName}}: {{#isEnum}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}{{{datatypeWithEnum}}}_{{operationId}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}? = nil{{/required}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}completion: @escaping (_ result: Swift.Result<Response<{{{returnType}}}{{^returnType}}Void{{/returnType}}>, ErrorResponse>) -> Void)
{{/useVapor}}
{{/useRxSwift}}
{{/usePromiseKit}}
Expand Down

0 comments on commit 2f281d0

Please sign in to comment.