Skip to content

Commit

Permalink
Merge pull request #89 from apivideo/feature/swift_rate_limit
Browse files Browse the repository at this point in the history
feat(swift5): add support for rate limit
  • Loading branch information
bot-api-video authored Apr 24, 2024
2 parents e6a124f + 4df56b4 commit 020d190
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 67 deletions.
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import ApiVideoUploader
// If you rather like to use the sandbox environment:
// ApiVideoUploader.basePath = Environment.sandbox.rawValue

try VideosAPI.uploadWithUploadToken(token: "MY_VIDEO_TOKEN", file: url) { video, error in
try VideosAPI.uploadWithUploadToken(token: "MY_UPLOAD_TOKEN", file: url) { video, error in
if let video = video {
// Manage upload with upload token success here
}
Expand Down Expand Up @@ -123,7 +123,27 @@ Method | HTTP request | Description
- [VideoSourceLiveStreamLink](https://github.com/apivideo/api.video-swift-uploader/blob/main/docs/VideoSourceLiveStreamLink.md)


### Documentation for Authorization
### Rate limiting

api.video implements rate limiting to ensure fair usage and stability of the service. The API provides the rate limit values in the response headers for any API requests you make. The /auth endpoint is the only route without rate limitation.

In this client, you can access these headers by using the methods with the `completion: @escaping (_ result: Swift.Result<Response<T>, ErrorResponse>) -> Void)` parameters. These methods return both the response body and the headers, allowing you to check the `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Retry-After` headers to understand your current rate limit status.
Read more about these response headers in the [API reference](https://docs.api.video/reference#limitation).

```swift
try VideosAPI.uploadWithUploadToken(token: "MY_UPLOAD_TOKEN", file: url) { result in
switch result {
case .success(let response):
print("X-RateLimit-Limit: \(String(describing: response.header["X-RateLimit-Limit"]))")
print("X-RateLimit-Remaining: \(String(describing: response.header["X-RateLimit-Remaining"]))")
print("X-RateLimit-Retry-After: \(String(describing: response.header["X-RateLimit-Retry-After"]))")
case .failure(_):
break
}
}
```

### Authorization

#### API key

Expand Down
56 changes: 40 additions & 16 deletions Sources/APIs/AdvancedAuthenticationAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,26 @@ open class AdvancedAuthenticationAPI {
*/
@discardableResult
open class func authenticate(authenticatePayload: AuthenticatePayload, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: AccessToken?, _ error: Error?) -> Void)) -> RequestTask {
return authenticateWithRequestBuilder(authenticatePayload: authenticatePayload).execute(apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
return authenticate(authenticatePayload: authenticatePayload, apiResponseQueue: apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
}

/**
Get Bearer Token
- parameter authenticatePayload: (body)
- parameter apiResponseQueue: The queue on which api response is dispatched.
- parameter completion: completion handler to receive the result of the request (incl. headers).
*/
@discardableResult
open class func authenticate(authenticatePayload: AuthenticatePayload, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<AccessToken>, ErrorResponse>) -> Void) -> RequestTask {
return authenticateWithRequestBuilder(authenticatePayload: authenticatePayload).execute(apiResponseQueue, completion)
}


Expand All @@ -39,7 +51,7 @@ open class AdvancedAuthenticationAPI {
- parameter authenticatePayload: (body)
- returns: RequestBuilder<AccessToken>
*/
open class func authenticateWithRequestBuilder(authenticatePayload: AuthenticatePayload) -> RequestBuilder<AccessToken> {
internal class func authenticateWithRequestBuilder(authenticatePayload: AuthenticatePayload) -> RequestBuilder<AccessToken> {
let localVariablePath = "/auth/api-key"
let localVariableURLString = ApiVideoUploader.basePath + localVariablePath
let localVariableParameters = JSONEncodingHelper.encodingParameters(forEncodableObject: authenticatePayload)
Expand Down Expand Up @@ -67,14 +79,26 @@ open class AdvancedAuthenticationAPI {
*/
@discardableResult
open class func refresh(refreshTokenPayload: RefreshTokenPayload, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: AccessToken?, _ error: Error?) -> Void)) -> RequestTask {
return refreshWithRequestBuilder(refreshTokenPayload: refreshTokenPayload).execute(apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
return refresh(refreshTokenPayload: refreshTokenPayload, apiResponseQueue: apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
}

/**
Refresh Bearer Token
- parameter refreshTokenPayload: (body)
- parameter apiResponseQueue: The queue on which api response is dispatched.
- parameter completion: completion handler to receive the result of the request (incl. headers).
*/
@discardableResult
open class func refresh(refreshTokenPayload: RefreshTokenPayload, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<AccessToken>, ErrorResponse>) -> Void) -> RequestTask {
return refreshWithRequestBuilder(refreshTokenPayload: refreshTokenPayload).execute(apiResponseQueue, completion)
}


Expand All @@ -85,7 +109,7 @@ open class AdvancedAuthenticationAPI {
- parameter refreshTokenPayload: (body)
- returns: RequestBuilder<AccessToken>
*/
open class func refreshWithRequestBuilder(refreshTokenPayload: RefreshTokenPayload) -> RequestBuilder<AccessToken> {
internal class func refreshWithRequestBuilder(refreshTokenPayload: RefreshTokenPayload) -> RequestBuilder<AccessToken> {
let localVariablePath = "/auth/refresh"
let localVariableURLString = ApiVideoUploader.basePath + localVariablePath
let localVariableParameters = JSONEncodingHelper.encodingParameters(forEncodableObject: refreshTokenPayload)
Expand Down
106 changes: 80 additions & 26 deletions Sources/APIs/VideosAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,31 @@ open class VideosAPI {
*/
@discardableResult
open class func upload(videoId: String, file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask {
return try upload(videoId: videoId, file: file, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
}

/**
Upload a video
- parameter videoId: (path) Enter the videoId you want to use to upload your video.
- parameter file: (form) The path to the video you would like to upload. The path must be local. If you want to use a video from an online source, you must use the \\\&quot;/videos\\\&quot; endpoint and add the \\\&quot;source\\\&quot; parameter when you create a new video.
- parameter onProgressReady: progress handler to receive request progress.
- parameter apiResponseQueue: The queue on which api response is dispatched.
- parameter completion: completion handler to receive the result of the request (incl. headers).
*/
@discardableResult
open class func upload(videoId: String, file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) throws -> RequestTask {
if (try file.isMultiChunk) {
return try UploadChunkRequestTaskQueue(videoId: videoId, file: file, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
} else {
return uploadWithRequestBuilder(videoId: videoId, file: file, onProgressReady: onProgressReady).execute(apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
return uploadWithRequestBuilder(videoId: videoId, file: file, onProgressReady: onProgressReady).execute(apiResponseQueue, completion)
}
}

Expand Down Expand Up @@ -78,13 +92,25 @@ open class VideosAPI {
}

public func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.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 = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) -> RequestTask {
var numOfChunks: Int? = nil
if (isLastPart) {
numOfChunks = partId
}
let requestBuilder = uploadWithRequestBuilder(videoId: videoId, file: file, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
completion(data, error)
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { result in
completion(result)
}
return requestBuilder.requestTask
}
Expand Down Expand Up @@ -121,7 +147,7 @@ The latter allows you to split a video source into X chunks and send those chunk
- parameter onProgressReady: progress handler to receive request progress.
- returns: RequestBuilder<Video>
*/
open class func uploadWithRequestBuilder(videoId: String, file: URL, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
internal class func uploadWithRequestBuilder(videoId: String, file: URL, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
var localVariablePath = "/videos/{videoId}/source"
let videoIdPreEscape = "\(APIHelper.mapValueToPathItem(videoId))"
let videoIdPostEscape = videoIdPreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
Expand Down Expand Up @@ -180,7 +206,7 @@ The latter allows you to split a video source into X chunks and send those chunk
- parameter onProgressReady: progress handler to receive request progress.
- returns: RequestBuilder<Video>
*/
open class func uploadWithRequestBuilder(videoId: String, file: FileChunkInputStream, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
internal class func uploadWithRequestBuilder(videoId: String, file: FileChunkInputStream, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
var localVariablePath = "/videos/{videoId}/source"
let videoIdPreEscape = "\(APIHelper.mapValueToPathItem(videoId))"
let videoIdPostEscape = videoIdPreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
Expand Down Expand Up @@ -220,17 +246,31 @@ The latter allows you to split a video source into X chunks and send those chunk
*/
@discardableResult
open class func uploadWithUploadToken(token: String, file: URL, videoId: String? = nil, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask {
return try uploadWithUploadToken(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
}

/**
Upload with an delegated upload token
- parameter token: (query) The unique identifier for the token you want to use to upload a video.
- parameter file: (form) The path to the video you want to upload.
- parameter onProgressReady: progress handler to receive request progress.
- parameter apiResponseQueue: The queue on which api response is dispatched.
- parameter completion: completion handler to receive the result of the request (incl. headers).
*/
@discardableResult
open class func uploadWithUploadToken(token: String, file: URL, videoId: String? = nil, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) throws -> RequestTask {
if (try file.isMultiChunk) {
return try UploadChunkRequestTaskQueue(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
} else {
return uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady).execute(apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
case let .failure(error):
completion(nil, error)
}
}
return uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady).execute(apiResponseQueue, completion)
}
}

Expand Down Expand Up @@ -282,18 +322,32 @@ The latter allows you to split a video source into X chunks and send those chunk
}

public func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.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 = ApiVideoUploader.apiResponseQueue, completion: @escaping (_ result: Swift.Result<Response<Video>, ErrorResponse>) -> Void) -> RequestTask {
var numOfChunks: Int? = nil
if (isLastPart) {
numOfChunks = partId
}
let requestBuilder = uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
if let data = data {
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { result in
switch result {
case let .success(response):
if self.videoId == nil {
self.videoId = data.videoId
self.videoId = response.body.videoId
}
case .failure(_): break
}
completion(data, error)
completion(result)
}
return requestBuilder.requestTask
}
Expand Down Expand Up @@ -325,7 +379,7 @@ The latter allows you to split a video source into X chunks and send those chunk
- parameter onProgressReady: progress handler to receive request progress.
- returns: RequestBuilder<Video>
*/
open class func uploadWithUploadTokenWithRequestBuilder(token: String, file: URL, videoId: String? = nil, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
internal class func uploadWithUploadTokenWithRequestBuilder(token: String, file: URL, videoId: String? = nil, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
let localVariablePath = "/upload"
let localVariableURLString = ApiVideoUploader.basePath + localVariablePath
var localVariableFormParams: [String: Any?] = [
Expand Down Expand Up @@ -369,7 +423,7 @@ The latter allows you to split a video source into X chunks and send those chunk
- parameter onProgressReady: progress handler to receive request progress.
- returns: RequestBuilder<Video>
*/
open class func uploadWithUploadTokenWithRequestBuilder(token: String, file: FileChunkInputStream, videoId: String? = nil, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
internal class func uploadWithUploadTokenWithRequestBuilder(token: String, file: FileChunkInputStream, videoId: String? = nil, chunkId: Int? = nil, numOfChunks: Int? = nil, onProgressReady: ((Progress) -> Void)? = nil) -> RequestBuilder<Video> {
let localVariablePath = "/upload"
let localVariableURLString = ApiVideoUploader.basePath + localVariablePath
var localVariableFormParams: [String: Any?] = [
Expand Down
Loading

0 comments on commit 020d190

Please sign in to comment.