diff --git a/OctoKit.xcodeproj/project.pbxproj b/OctoKit.xcodeproj/project.pbxproj index 120b842..6540ac8 100644 --- a/OctoKit.xcodeproj/project.pbxproj +++ b/OctoKit.xcodeproj/project.pbxproj @@ -1720,7 +1720,7 @@ repositoryURL = "https://github.com/nerdishbynature/RequestKit.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 3.2.1; + minimumVersion = 3.3.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/OctoKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/OctoKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 51e06cc..1dfd9f8 100644 --- a/OctoKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/OctoKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,16 +1,15 @@ { - "object": { - "pins": [ - { - "package": "RequestKit", - "repositoryURL": "https://github.com/nerdishbynature/RequestKit.git", - "state": { - "branch": null, - "revision": "8b0258ea2a4345cbcac90509b764faacea12efb0", - "version": "3.2.1" - } + "originHash" : "530774d6f20b0f00dacedf9498eab8ef4fc75274d7d10f28ee2fdbb4aab6bbb8", + "pins" : [ + { + "identity" : "requestkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nerdishbynature/RequestKit.git", + "state" : { + "revision" : "e4d905fed938807e36d87f28375f88b7c1c26840", + "version" : "3.3.0" } - ] - }, - "version": 1 + } + ], + "version" : 3 } diff --git a/OctoKit/Review.swift b/OctoKit/Review.swift index 45cda9c..aa9a088 100644 --- a/OctoKit/Review.swift +++ b/OctoKit/Review.swift @@ -9,7 +9,7 @@ public struct Review { public let commitID: String public let id: Int public let state: State - public let submittedAt: Date + public let submittedAt: Date? public let user: User public init(body: String, @@ -116,6 +116,40 @@ public extension Octokit { } } + @discardableResult + func deletePendingReview(owner: String, + repository: String, + pullRequestNumber: Int, + reviewId: Int, + completion: @escaping (_ response: Result) -> Void) -> URLSessionDataTaskProtocol? { + let router = ReviewsRouter.deletePendingReview(configuration, owner, repository, pullRequestNumber, reviewId) + return router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) { pullRequest, error in + if let error = error { + completion(.failure(error)) + } else if let pullRequest = pullRequest { + completion(.success(pullRequest)) + } + } + } + + @discardableResult + func submitReview(owner: String, + repository: String, + pullRequestNumber: Int, + reviewId: Int, + event: Review.Event, + body: String? = nil, + completion: @escaping (_ response: Result) -> Void) -> URLSessionDataTaskProtocol? { + let router = ReviewsRouter.submitReview(configuration, owner, repository, pullRequestNumber, reviewId, event, body) + return router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) { pullRequest, error in + if let error = error { + completion(.failure(error)) + } else if let pullRequest = pullRequest { + completion(.success(pullRequest)) + } + } + } + #if compiler(>=5.5.2) && canImport(_Concurrency) @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) func reviews(owner: String, @@ -135,7 +169,29 @@ public extension Octokit { body: String? = nil, comments: [Review.Comment] = []) async throws -> Review { let router = ReviewsRouter.postReview(configuration, owner, repository, pullRequestNumber, commitId, event, body, comments) - return try await router.load(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) + return try await router.post(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) + } + + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + @discardableResult + func deletePendingReview(owner: String, + repository: String, + pullRequestNumber: Int, + reviewId: Int) async throws -> Review { + let router = ReviewsRouter.deletePendingReview(configuration, owner, repository, pullRequestNumber, reviewId) + return try await router.post(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) + } + + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + @discardableResult + func submitReview(owner: String, + repository: String, + pullRequestNumber: Int, + reviewId: Int, + event: Review.Event, + body: String? = nil) async throws -> Review { + let router = ReviewsRouter.submitReview(configuration, owner, repository, pullRequestNumber, reviewId, event, body) + return try await router.post(session, dateDecodingStrategy: .formatted(Time.rfc3339DateFormatter), expectedResultType: Review.self) } #endif } @@ -143,6 +199,8 @@ public extension Octokit { enum ReviewsRouter: JSONPostRouter { case listReviews(Configuration, String, String, Int) case postReview(Configuration, String, String, Int, String?, Review.Event?, String?, [Review.Comment]?) + case deletePendingReview(Configuration, String, String, Int, Int) + case submitReview(Configuration, String, String, Int, Int, Review.Event, String?) var method: HTTPMethod { switch self { @@ -150,12 +208,16 @@ enum ReviewsRouter: JSONPostRouter { return .GET case .postReview: return .POST + case .deletePendingReview: + return .DELETE + case .submitReview: + return .POST } } var encoding: HTTPEncoding { switch self { - case .postReview: + case .postReview, .deletePendingReview, .submitReview: return .json default: return .url @@ -168,12 +230,16 @@ enum ReviewsRouter: JSONPostRouter { return config case let .postReview(config, _, _, _, _, _, _, _): return config + case let .deletePendingReview(config, _, _, _, _): + return config + case let .submitReview(config, _, _, _, _, _, _): + return config } } var params: [String: Any] { switch self { - case .listReviews: + case .listReviews, .deletePendingReview: return [:] case let .postReview(_, _, _, _, commitId, event, body, comments): var parameters = [String: Any]() @@ -211,6 +277,14 @@ enum ReviewsRouter: JSONPostRouter { } } return parameters + case let .submitReview(_, _, _, _, _, event, body): + var parameters: [String: Any] = [ + "event": event.rawValue + ] + if let body = body { + parameters["body"] = body + } + return parameters } } @@ -220,6 +294,10 @@ enum ReviewsRouter: JSONPostRouter { return "repos/\(owner)/\(repository)/pulls/\(pullRequestNumber)/reviews" case let .postReview(_, owner, repository, pullRequestNumber, _, _, _, _): return "repos/\(owner)/\(repository)/pulls/\(pullRequestNumber)/reviews" + case let .deletePendingReview(_, owner, repository, pullRequestNumber, reviewId): + return "repos/\(owner)/\(repository)/pulls/\(pullRequestNumber)/reviews/\(reviewId)" + case let .submitReview(_, owner, repository, pullRequestNumber, reviewId, _, _): + return "repos/\(owner)/\(repository)/pulls/\(pullRequestNumber)/reviews/\(reviewId)/events" } } } diff --git a/Package.resolved b/Package.resolved index 2500a12..95a48fe 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/nerdishbynature/RequestKit.git", "state": { "branch": null, - "revision": "8b0258ea2a4345cbcac90509b764faacea12efb0", - "version": "3.2.1" + "revision": "e4d905fed938807e36d87f28375f88b7c1c26840", + "version": "3.3.0" } }, { diff --git a/Package.swift b/Package.swift index 4e82622..7ff4c2c 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/nerdishbynature/RequestKit.git", from: "3.2.1"), + .package(url: "https://github.com/nerdishbynature/RequestKit.git", from: "3.3.0"), .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.52.8") ], targets: [ diff --git a/Tests/OctoKitTests/ReviewTests.swift b/Tests/OctoKitTests/ReviewTests.swift index 22c3780..4bd5df1 100644 --- a/Tests/OctoKitTests/ReviewTests.swift +++ b/Tests/OctoKitTests/ReviewTests.swift @@ -133,4 +133,134 @@ class ReviewTests: XCTestCase { XCTAssertTrue(session.wasCalled) } #endif + + func testDeletePendingReview() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/octocat/Hello-World/pulls/1/reviews/80", expectedHTTPMethod: "DELETE", jsonFile: "review", statusCode: 200) + let task = Octokit(session: session).deletePendingReview(owner: "octocat", + repository: "Hello-World", + pullRequestNumber: 1, + reviewId: 80) { + switch $0 { + case let .success(review): + XCTAssertEqual(review.body, "Here is the body for the review.") + XCTAssertEqual(review.commitID, "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091") + XCTAssertEqual(review.id, 80) + XCTAssertEqual(review.state, .approved) + XCTAssertEqual(review.submittedAt, Date(timeIntervalSince1970: 1_574_012_623.0)) + XCTAssertEqual(review.user.avatarURL, "https://github.com/images/error/octocat_happy.gif") + XCTAssertNil(review.user.blog) + XCTAssertNil(review.user.company) + XCTAssertNil(review.user.email) + XCTAssertEqual(review.user.gravatarID, "") + XCTAssertEqual(review.user.id, 1) + XCTAssertNil(review.user.location) + XCTAssertEqual(review.user.login, "octocat") + XCTAssertNil(review.user.name) + XCTAssertNil(review.user.numberOfPublicGists) + XCTAssertNil(review.user.numberOfPublicRepos) + XCTAssertNil(review.user.numberOfPrivateRepos) + XCTAssertEqual(review.user.type, "User") + case .failure: + XCTFail("should not get an error") + } + } + XCTAssertNotNil(task) + XCTAssertTrue(session.wasCalled) + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testDeletePendingReviewAsync() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/octocat/Hello-World/pulls/1/reviews/80", expectedHTTPMethod: "DELETE", jsonFile: "review", statusCode: 200) + let review = try await Octokit(session: session).deletePendingReview(owner: "octocat", + repository: "Hello-World", + pullRequestNumber: 1, + reviewId: 80) + XCTAssertEqual(review.body, "Here is the body for the review.") + XCTAssertEqual(review.commitID, "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091") + XCTAssertEqual(review.id, 80) + XCTAssertEqual(review.state, .approved) + XCTAssertEqual(review.submittedAt, Date(timeIntervalSince1970: 1_574_012_623.0)) + XCTAssertEqual(review.user.avatarURL, "https://github.com/images/error/octocat_happy.gif") + XCTAssertNil(review.user.blog) + XCTAssertNil(review.user.company) + XCTAssertNil(review.user.email) + XCTAssertEqual(review.user.gravatarID, "") + XCTAssertEqual(review.user.id, 1) + XCTAssertNil(review.user.location) + XCTAssertEqual(review.user.login, "octocat") + XCTAssertNil(review.user.name) + XCTAssertNil(review.user.numberOfPublicGists) + XCTAssertNil(review.user.numberOfPublicRepos) + XCTAssertNil(review.user.numberOfPrivateRepos) + XCTAssertEqual(review.user.type, "User") + XCTAssertTrue(session.wasCalled) + } + #endif + + func testSubmitReview() { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/octocat/Hello-World/pulls/1/reviews/80/events", expectedHTTPMethod: "POST", jsonFile: "review", statusCode: 200) + let task = Octokit(session: session).submitReview(owner: "octocat", + repository: "Hello-World", + pullRequestNumber: 1, + reviewId: 80, + event: .approve) { + switch $0 { + case let .success(review): + XCTAssertEqual(review.body, "Here is the body for the review.") + XCTAssertEqual(review.commitID, "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091") + XCTAssertEqual(review.id, 80) + XCTAssertEqual(review.state, .approved) + XCTAssertEqual(review.submittedAt, Date(timeIntervalSince1970: 1_574_012_623.0)) + XCTAssertEqual(review.user.avatarURL, "https://github.com/images/error/octocat_happy.gif") + XCTAssertNil(review.user.blog) + XCTAssertNil(review.user.company) + XCTAssertNil(review.user.email) + XCTAssertEqual(review.user.gravatarID, "") + XCTAssertEqual(review.user.id, 1) + XCTAssertNil(review.user.location) + XCTAssertEqual(review.user.login, "octocat") + XCTAssertNil(review.user.name) + XCTAssertNil(review.user.numberOfPublicGists) + XCTAssertNil(review.user.numberOfPublicRepos) + XCTAssertNil(review.user.numberOfPrivateRepos) + XCTAssertEqual(review.user.type, "User") + case .failure: + XCTFail("should not get an error") + } + } + XCTAssertNotNil(task) + XCTAssertTrue(session.wasCalled) + } + + #if compiler(>=5.5.2) && canImport(_Concurrency) + @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) + func testSubmitReviewAsync() async throws { + let session = OctoKitURLTestSession(expectedURL: "https://api.github.com/repos/octocat/Hello-World/pulls/1/reviews/80/events", expectedHTTPMethod: "POST", jsonFile: "review", statusCode: 200) + let review = try await Octokit(session: session).submitReview(owner: "octocat", + repository: "Hello-World", + pullRequestNumber: 1, + reviewId: 80, + event: .approve) + XCTAssertEqual(review.body, "Here is the body for the review.") + XCTAssertEqual(review.commitID, "ecdd80bb57125d7ba9641ffaa4d7d2c19d3f3091") + XCTAssertEqual(review.id, 80) + XCTAssertEqual(review.state, .approved) + XCTAssertEqual(review.submittedAt, Date(timeIntervalSince1970: 1_574_012_623.0)) + XCTAssertEqual(review.user.avatarURL, "https://github.com/images/error/octocat_happy.gif") + XCTAssertNil(review.user.blog) + XCTAssertNil(review.user.company) + XCTAssertNil(review.user.email) + XCTAssertEqual(review.user.gravatarID, "") + XCTAssertEqual(review.user.id, 1) + XCTAssertNil(review.user.location) + XCTAssertEqual(review.user.login, "octocat") + XCTAssertNil(review.user.name) + XCTAssertNil(review.user.numberOfPublicGists) + XCTAssertNil(review.user.numberOfPublicRepos) + XCTAssertNil(review.user.numberOfPrivateRepos) + XCTAssertEqual(review.user.type, "User") + XCTAssertTrue(session.wasCalled) + } + #endif }