Skip to content

Commit

Permalink
Merge pull request #43 from apivideo/feature/upload-by-video-id-and-u…
Browse files Browse the repository at this point in the history
…pload-token

Upload by video id and upload token
  • Loading branch information
bot-api-video authored Feb 10, 2023
2 parents 4adff12 + 0328e05 commit 1da5549
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 69 deletions.
71 changes: 9 additions & 62 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@ The following is a set of guidelines for contributing to api.video and its packa
- [Suggesting enhancements](#suggesting-enhancements)
- [How do I submit a (good) enhancement suggestion?](#how-do-i-submit-a-good-enhancement-suggestion)
- [Pull requests](#pull-requests)
- [Style guides](#style-guides)
- [Git commit messages](#git-commit-messages)
- [Documentation style guide](#documentation-style-guide)
- [Additional notes](#additional-notes)
- [Issue and pull request labels](#issue-and-pull-request-labels)
- [Issue labels](#issue-labels)
- [Type of issue and issue state](#type-of-issue-and-issue-state)
- [Topic categories](#topic-categories)
- [Pull request labels](#pull-request-labels)


## Code of conduct

This project and everyone participating in it is governed by the [api.video Code of Conduct](https://github.com/apivideo/.github/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [help@api.video](mailto:help@api.video).
This project and everyone participating in it is governed by the [api.video Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [help@api.video](mailto:help@api.video).

## I just have a question!

Expand Down Expand Up @@ -104,55 +100,15 @@ Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com

### Pull requests

The process described here has several goals:

- Maintain api.video's quality
- Fix problems that are important to users
- Engage the community in working toward the best possible api.video
- Enable a sustainable system for api.video's maintainers to review contributions

Please follow these steps to have your contribution considered by the maintainers:

1. Explain what, why and how you resolved the issue. If you have a related issue, please mention it.
2. Follow the [style guides](#style-guides)
3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing <details><summary>What if the status checks are failing?</summary>If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.</details>

While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted.

## Style guides

### Git commit messages

* Use the present tense ("Add feature" not "Added feature")
* Limit the first line to 72 characters or less
* Reference issues and pull requests after the first line
* Consider starting the commit message with an applicable emoji:
* :art: `:art:` when improving the format/structure of the code
* :racehorse: `:racehorse:` when improving performance
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
* :memo: `:memo:` when writing docs
* :penguin: `:penguin:` when fixing something on Linux
* :apple: `:apple:` when fixing something on macOS
* :checkered_flag: `:checkered_flag:` when fixing something on Windows
* :bug: `:bug:` when fixing a bug
* :fire: `:fire:` when removing code or files
* :green_heart: `:green_heart:` when fixing the CI build
* :white_check_mark: `:white_check_mark:` when adding tests
* :lock: `:lock:` when dealing with security
* :arrow_up: `:arrow_up:` when upgrading dependencies
* :arrow_down: `:arrow_down:` when downgrading dependencies
* :shirt: `:shirt:` when removing linter warnings

### Documentation style guide

* Use [Markdown](https://daringfireball.net/projects/markdown).

Since this API client is generated from an OpenAPI description, we cannot accept pull requests made directly to the repository.
If you want to contribute, you can open a pull request on the repository of our [client generator](https://github.com/apivideo/api.video-api-client-generator).
Otherwise, you can also simply open an issue detailing your need on this repository.

## Additional notes

### Issue and pull request labels
### Issue labels

This section lists the labels we use to help us track and manage issues and pull requests on all api.video repositories.
This section lists the labels we use to help us track and manage issues on all api.video repositories.

[GitHub search](https://help.github.com/articles/searching-issues/) makes it easy to use labels for finding groups of issues or pull requests you're interested in. We encourage you to read about [other search filters](https://help.github.com/articles/searching-issues/) which will help you write more focused queries.

Expand Down Expand Up @@ -188,15 +144,6 @@ This section lists the labels we use to help us track and manage issues and pull
| `ui` | [search][search-apivideo-org-label-ui] | Related to visual design. |
| `api` | [search][search-apivideo-org-label-api] | Related to api.video's public APIs. |

#### Pull request labels

| Label name | `apivideo` :mag_right: | Description
| --- | --- | --- |
| `work-in-progress` | [search][search-apivideo-org-label-work-in-progress] | Pull requests which are still being worked on, more changes will follow. |
| `needs-review` | [search][search-apivideo-org-label-needs-review] | Pull requests which need code review, and approval from maintainers or api.video team. |
| `under-review` | [search][search-apivideo-org-label-under-review] | Pull requests being reviewed by maintainers or api.video team. |
| `requires-changes` | [search][search-apivideo-org-label-requires-changes] | Pull requests which need to be updated based on review comments and then reviewed again. |
| `needs-testing` | [search][search-apivideo-org-label-needs-testing] | Pull requests which need manual testing. |

[search-apivideo-org-label-enhancement]: https://github.com/search?q=is%3Aopen+is%3Aissue+user%3Aapivideo+label%3Aenhancement
[search-apivideo-org-label-bug]: https://github.com/search?q=is%3Aopen+is%3Aissue+user%3Aapivideo+label%3Abug
Expand Down Expand Up @@ -225,4 +172,4 @@ This section lists the labels we use to help us track and manage issues and pull
[search-apivideo-org-label-requires-changes]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aapivideo%2Fapivideo+label%3Arequires-changes
[search-apivideo-org-label-needs-testing]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aapivideo%2Fapivideo+label%3Aneeds-testing

[help-wanted]:https://github.com/search?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aapivideo+sort%3Acomments-desc
[help-wanted]:https://github.com/search?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aapivideo+sort%3Acomments-desc
15 changes: 9 additions & 6 deletions Sources/APIs/VideosAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@ The latter allows you to split a video source into X chunks and send those chunk
- parameter completion: completion handler to receive the data and the error objects.
*/
@discardableResult
open class func uploadWithUploadToken(token: String, file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask {
open class func uploadWithUploadToken(token: String, file: URL, videoId: String? = nil, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask {
if (try file.isMultiChunk) {
return try UploadChunkRequestTaskQueue(token: token, file: file, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
return try UploadChunkRequestTaskQueue(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
} else {
return uploadWithUploadTokenWithRequestBuilder(token: token, file: file, onProgressReady: onProgressReady).execute(apiResponseQueue) { result in
return uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, onProgressReady: onProgressReady).execute(apiResponseQueue) { result in
switch result {
case let .success(response):
completion(response.body, nil)
Expand All @@ -283,7 +283,7 @@ The latter allows you to split a video source into X chunks and send those chunk
*
- returns: a progressive uploadWithUploadToken session
*/
public class func buildProgressiveUploadWithUploadTokenSession(token: String) -> ProgressiveUploadWithUploadTokenSession {
public class func buildProgressiveUploadWithUploadTokenSession(token: String, videoId: String? = nil) -> ProgressiveUploadWithUploadTokenSession {
ProgressiveUploadWithUploadTokenSession(token: token)
}

Expand All @@ -293,8 +293,9 @@ The latter allows you to split a video source into X chunks and send those chunk

private let token: String

public init(token: String) {
public init(token: String, videoId: String? = nil) {
self.token = token
self.videoId = videoId
super.init(queueLabel: token)
}

Expand Down Expand Up @@ -332,7 +333,9 @@ The latter allows you to split a video source into X chunks and send those chunk
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 {
self.videoId = data.videoId
if self.videoId == nil {
self.videoId = data.videoId
}
}
completion(data, error)
}
Expand Down
6 changes: 5 additions & 1 deletion Sources/Upload/UploadChunkRequestTaskQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {

internal convenience init(token: String,
file: URL,
videoId: String? = nil,
onProgressReady: ((Progress) -> Void)? = nil,
apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue,
completion: @escaping (_ data: Video?, _ error: Error?) -> Void) throws {
Expand All @@ -65,6 +66,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
requestBuilders.append(requestBuilder)
}
try self.init(requestBuilders: requestBuilders, fileSize: file.fileSize, queueLabel: token, onProgressReady: onProgressReady, apiResponseQueue: apiResponseQueue, completion: completion)
self.videoId = videoId
}

override func willExecuteRequestBuilder(requestBuilder: RequestBuilder<Video>) -> Void {
Expand Down Expand Up @@ -93,7 +95,9 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
completion(nil, error)
}
} else {
videoId = data?.videoId
if (videoId == nil) {
videoId = data?.videoId
}
if (requestBuilders.allSatisfy {
$0.requestTask.state == .finished
}) {
Expand Down
157 changes: 157 additions & 0 deletions Tests/ApiVideoClient/Integration/VideosApiTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,163 @@ class ProgressiveUploadWithTokenTests: UploadWithTokenTestCase {
}
}

internal class UploadWithTokenAndVideoIdTestCase: XCTestCase {
internal var token: String? = nil
internal var videoId: String? = nil

override func setUpWithError() throws {
try super.setUpWithError()
try XCTSkipIf(Parameters.apiKey == "INTEGRATION_TESTS_API_KEY", "Can't get API key")
ApiVideoClient.apiKey = Parameters.apiKey
ApiVideoClient.basePath = Environment.sandbox.rawValue

continueAfterFailure = false
}

override func tearDown() {
super.tearDown()

if let token = self.token {
UploadTokensAPI.deleteToken(uploadToken: token) { data, error in
}
}
if let videoId = self.videoId {
VideosAPI.delete(videoId: videoId) { data, error in
}
}
}

internal func createUploadToken() {
let expectation = XCTestExpectation(description: "Create an upload token")

UploadTokensAPI.createToken(tokenCreationPayload: TokenCreationPayload(ttl: 120)) { uploadToken, error in
XCTAssertNil(error, "Failed to create a an upload token due to \(String(describing: error))")
XCTAssertNotNil(uploadToken, "Failed to create an upload token")
if let uploadToken = uploadToken {
self.token = uploadToken.token
}
expectation.fulfill()
}

wait(for: [expectation], timeout: 5)
}

internal func createVideo() {
let expectation = XCTestExpectation(description: "Create a video")

VideosAPI.create(videoCreationPayload: VideoCreationPayload(title: "[iOS-API-client-tests] \(self.name)")) { video, error in
XCTAssertNil(error, "Failed to create a video due to \(String(describing: error))")
XCTAssertNotNil(video, "Failed to create a video")
if let video = video {
self.videoId = video.videoId
}
expectation.fulfill()
}

wait(for: [expectation], timeout: 5)
}

internal func uploadVideo(file: URL, timeout: TimeInterval = 30) {
let expectation = XCTestExpectation(description: "Upload a video")
var observedProgress: Progress? = nil

do {
try VideosAPI.uploadWithUploadToken(token: self.token!, file: file, videoId: self.videoId!, onProgressReady: { progress in
observedProgress = progress
}) { video, error in
XCTAssertNotNil(video, "Failed to upload a video")
XCTAssertNil(error, "Failed to upload a video due to \(String(describing: error))")
expectation.fulfill()
}
} catch {
XCTFail("Failed to upload a video with upload token due to \(error)")
}

wait(for: [expectation], timeout: timeout)

XCTAssertNotNil(observedProgress, "Failed to get progress")
XCTAssertEqual(1.0, observedProgress?.fractionCompleted)
}
}

class SingleUploadWithTokenAndVideoIdTests: UploadWithTokenAndVideoIdTestCase {
func testUpload() {
createUploadToken()
createVideo()

uploadVideo(file: SharedResources.v558k!)
}
}

class ProgressiveUploadWithTokenAndVideoIdTests: UploadWithTokenAndVideoIdTestCase {
private var progressiveUploadSession: VideosAPI.ProgressiveUploadWithUploadTokenSession? = nil

@discardableResult
private func uploadPart(file: URL, isLastPart: Bool = false, timeout: TimeInterval = 60, expectation: XCTestExpectation = XCTestExpectation(description: "Upload a video part")) -> RequestTask {
var observedProgress: Progress? = nil
var requestTask: RequestTask!

if (isLastPart) {
requestTask = progressiveUploadSession!.uploadLastPart(file: file, onProgressReady: { progress in
observedProgress = progress
}) { video, error in
XCTAssertNil(error, "Failed to upload a video due to \(String(describing: error))")
XCTAssertNotNil(video, "Failed to upload a video")
if (error == nil) {
XCTAssertNotNil(observedProgress, "Failed to get progress")
XCTAssertEqual(1.0, observedProgress?.fractionCompleted)
}
expectation.fulfill()
}
} else {
requestTask = progressiveUploadSession!.uploadPart(file: file, onProgressReady: { progress in
observedProgress = progress
}) { video, error in
XCTAssertNil(error, "Failed to upload a video due to \(String(describing: error))")
XCTAssertNotNil(video, "Failed to upload a video")
if (error == nil) {
XCTAssertNotNil(observedProgress, "Failed to get progress")
XCTAssertEqual(1.0, observedProgress?.fractionCompleted)
}
expectation.fulfill()
}
}

if (timeout > 0) {
wait(for: [expectation], timeout: timeout)
}

return requestTask
}


/// Wait for a part to be uploaded before trying to upload a new part
func testSyncUpload() {
createUploadToken()
createVideo()

progressiveUploadSession = VideosAPI.buildProgressiveUploadWithUploadTokenSession(token: self.token!, videoId: self.videoId!)
uploadPart(file: SharedResources.v10m_parta!)
uploadPart(file: SharedResources.v10m_partb!)
uploadPart(file: SharedResources.v10m_partc!, isLastPart: true)
}

/// Add all the part to the upload queue
func testStackedUpload() {
createUploadToken()
createVideo()

let expectation = XCTestExpectation(description: "Upload a video part")
expectation.expectedFulfillmentCount = 3

progressiveUploadSession = VideosAPI.buildProgressiveUploadWithUploadTokenSession(token: self.token!, videoId: self.videoId!)
uploadPart(file: SharedResources.v10m_parta!, timeout: 0, expectation: expectation)
uploadPart(file: SharedResources.v10m_partb!, timeout: 0, expectation: expectation)
uploadPart(file: SharedResources.v10m_partc!, isLastPart: true, timeout: 0, expectation: expectation)
wait(for: [expectation], timeout: 180)
}
}

class UpdateTests: XCTestCase {
func testPlayerIdEncoding() {
do {
Expand Down

0 comments on commit 1da5549

Please sign in to comment.