├── .gitignore ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md └── Sources ├── GitHubKit ├── Exports.swift ├── GitHub.swift ├── GitHubClient.swift ├── GitHubServer.swift ├── Model │ ├── Author.swift │ ├── Branch.swift │ ├── Comment.swift │ ├── Commit.swift │ ├── File.swift │ ├── Git │ │ ├── GitBlob.swift │ │ ├── GitCommit.swift │ │ └── GitTree.swift │ ├── License.swift │ ├── Milestone.swift │ ├── Organization.swift │ ├── Owner.swift │ ├── PR.swift │ ├── Release.swift │ ├── Repo.swift │ ├── Tag.swift │ ├── Verification.swift │ └── Webhook.swift ├── Query.swift └── Requests │ ├── Branch+Requests.swift │ ├── Comment+Requests.swift │ ├── Commit+Requests.swift │ ├── File+Requests.swift │ ├── Git │ ├── GitBlob+Requests.swift │ ├── GitCommit+Requests.swift │ └── GitTree+Requests.swift │ ├── Organization+Requests.swift │ ├── PR+Requests.swift │ ├── Release+Requests.swift │ ├── Repo+Requests.swift │ ├── Tag+Requests.swift │ └── Webhook+Requests.swift ├── GitHubKitMocks ├── Exports.swift └── GitHubMock.swift └── GitHubKitMocksTests └── Tests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | /.swiftpm 70 | .DS_Store 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Einstore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "async-http-client", 6 | "repositoryURL": "https://github.com/swift-server/async-http-client.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "037b70291941fe43de668066eb6fb802c5e181d2", 10 | "version": "1.1.1" 11 | } 12 | }, 13 | { 14 | "package": "swift-nio", 15 | "repositoryURL": "https://github.com/apple/swift-nio.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "e876fb37410e0036b98b5361bb18e6854739572b", 19 | "version": "2.16.0" 20 | } 21 | }, 22 | { 23 | "package": "swift-nio-extras", 24 | "repositoryURL": "https://github.com/apple/swift-nio-extras.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "b4dbfacff47fb8d0f9e0a422d8d37935a9f10570", 28 | "version": "1.4.0" 29 | } 30 | }, 31 | { 32 | "package": "swift-nio-ssl", 33 | "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "ae213938e151964aa691f0e902462fbe06baeeb6", 37 | "version": "2.7.1" 38 | } 39 | } 40 | ] 41 | }, 42 | "version": 1 43 | } 44 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "GitHubKit", 6 | products: [ 7 | .library(name: "GitHubKit", targets: ["GitHubKit"]), 8 | .library(name: "GitHubKitMocks", targets: ["GitHubKitMocks"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.1.1") 12 | ], 13 | targets: [ 14 | .target( 15 | name: "GitHubKit", 16 | dependencies: [ 17 | "AsyncHTTPClient" 18 | ] 19 | ), 20 | .target( 21 | name: "GitHubKitMocks", 22 | dependencies: [ 23 | "GitHubKit" 24 | ] 25 | ), 26 | .testTarget( 27 | name: "GitHubKitMocksTests", 28 | dependencies: [ 29 | "GitHubKitMocks" 30 | ] 31 | ) 32 | ] 33 | ) 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHubKit 2 | 3 | [![Slack](https://img.shields.io/badge/join-slack-745EAF.svg?style=flat)](https://bit.ly/2UkyFO8) 4 | [![iOS](https://img.shields.io/badge/iOS-ff0000.svg?style=flat)](https://github.com/Einstore/Einstore) 5 | [![macOS](https://img.shields.io/badge/macOS-ff0000.svg?style=flat)](https://github.com/Einstore/Einstore) 6 | [![Ubuntu](https://img.shields.io/badge/Ubuntu-D95E33.svg?style=flat)](https://www.ubuntu.com/download/server) 7 | [![Swift 5.1](https://img.shields.io/badge/swift-5.1-orange.svg?style=flat)](http://swift.org) 8 | [![Vapor 4](https://img.shields.io/badge/vapor-4.0-blue.svg?style=flat)](https://vapor.codes) 9 | 10 | Super simple to use Github API client library written using Apple NIO 11 | 12 | ## Functionality 13 | 14 | The below probably don't contain all methods from the spec but it's a good start ... 15 | 16 | - [x] Organizations 17 | - [x] Repos 18 | - [x] Files 19 | - [x] File contents 20 | - [x] Comments 21 | - [x] Commits (full management) 22 | - [x] Branches 23 | - [x] Webhooks 24 | - [x] Tags 25 | - [x] Releases 26 | - [ ] PR's 27 | - [ ] Issues 28 | 29 | ## Usage 30 | 31 | #### Add to your Package.swift file 32 | 33 | ```swift 34 | .package(url: "https://github.com/Einstore/GitHubKit.git", from: "1.0.0") 35 | ``` 36 | 37 | Don't forget about your target 38 | 39 | ```swift 40 | .target( 41 | name: "App", 42 | x: [ 43 | "GitHubKit" 44 | ] 45 | ) 46 | ``` 47 | 48 | #### Configure 49 | ```swift 50 | let config = Github.Config( 51 | username: "orafaj", 52 | token: token, 53 | server: "https://github.ford.com/api/v3/" 54 | ) 55 | let github = try Github(config) 56 | 57 | // or 58 | 59 | let github = try Github(config, eventLoop: eventLoop) 60 | 61 | // or 62 | 63 | let github = try Github(config, eventLoopGroupProvider: .createNew) 64 | ``` 65 | 66 | #### Use in iOS and server side frameworks 67 | 68 | Although this library has been only tested with Vapor 4, using Apple NIO HHTPClient should be compatible with any iOS or server side project. 69 | 70 | Please let us know on our [slack here](https://bit.ly/2UkyFO8) how you getting on or should you need any support! 71 | 72 | #### Use in Vapor 4? 73 | 74 | ```swift 75 | import GitHubKit 76 | 77 | // In configure.swift 78 | services.register(Github.self) { container in 79 | let config = Github.Config( 80 | username: "orafaj", 81 | token: token, 82 | server: "https://github.ford.com/api/v3/" 83 | ) 84 | return try Github(config, eventLoop: container.eventLoop) 85 | } 86 | 87 | // In routes (or a controller) 88 | r.get("github", "organizations") { req -> EventLoopFuture<[Organization]> in 89 | let github = try c.make(Github.self) 90 | return try Organization.query(on: github).getAll().map() { orgs in 91 | print(orgs) 92 | return orgs 93 | } 94 | } 95 | ``` 96 | 97 | ## Development 98 | 99 | Adding a new API call is ... surprisingly super simple too 100 | 101 | Lets say you need to add a detail of a user 102 | 103 | #### Go to the documentation 104 | 105 | https://developer.github.com/v3/users/ 106 | 107 | #### Autogenerate a model 108 | 109 | Copy the example JSON, for example: 110 | 111 | ```json 112 | { 113 | "login": "octocat", 114 | "id": 1, 115 | "node_id": "MDQ6VXNlcjE=", 116 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 117 | "gravatar_id": "", 118 | "url": "https://api.github.com/users/octocat", 119 | "html_url": "https://github.com/octocat", 120 | "followers_url": "https://api.github.com/users/octocat/followers", 121 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 122 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 123 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 124 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 125 | "organizations_url": "https://api.github.com/users/octocat/orgs", 126 | "repos_url": "https://api.github.com/users/octocat/repos", 127 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 128 | "received_events_url": "https://api.github.com/users/octocat/received_events", 129 | "type": "User", 130 | "site_admin": false, 131 | "name": "monalisa octocat", 132 | "company": "GitHub", 133 | "blog": "https://github.com/blog", 134 | "location": "San Francisco", 135 | "email": "octocat@github.com", 136 | "hireable": false, 137 | "bio": "There once was...", 138 | "public_repos": 2, 139 | "public_gists": 1, 140 | "followers": 20, 141 | "following": 0, 142 | "created_at": "2008-01-14T04:33:35Z", 143 | "updated_at": "2008-01-14T04:33:35Z" 144 | } 145 | ``` 146 | 147 | Go to the https://app.quicktype.io and convert the JSON into a model ... you might want to mess a bit with the settings to keep the models consistent with ones in the project already. Also, any sub structs (unless they can be used elsewhere) should be moved inside of the parent model. 148 | 149 | Oh yeah ... and call the main class `User`! :) ... 150 | 151 | Import `Vapor` and conform the main model to `Content` instead of `Codable`. 152 | 153 | #### Make a request extension 154 | 155 | First you need to conform the `User` model to `Queryable`. This will enable the `User.query(on: container)` method. 156 | 157 | ```swift 158 | extension User: Queryable { } 159 | ``` 160 | 161 | Next we create an extension on `QueryableProperty` which is generated each time you request a query on a container from the previous step. Make sure you specify the `QueryableType == User` 162 | 163 | ```swift 164 | extension QueryableProperty where QueryableType == User { 165 | 166 | /// Get all organizations for authenticated user 167 | public func get() throws -> EventLoopFuture { 168 | return try github.get(path: "user") 169 | } 170 | 171 | } 172 | ``` 173 | 174 | #### All done 175 | 176 | You should be able to call `try User.query(on: c).get()` and get an `EventLoopFuture` with the details of your authenticated user. 177 | 178 | ### Author 179 | 180 | **Ondrej Rafaj** @rafiki270 181 | 182 | (It wasn't my token after all, was it?!) 183 | 184 | ### License 185 | 186 | MIT; See LICENSE file for details. 187 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Exports.swift: -------------------------------------------------------------------------------- 1 | @_exported import Foundation 2 | @_exported import class NIO.EventLoopFuture 3 | @_exported import class NIO.EmbeddedEventLoop 4 | @_exported import enum NIOHTTP1.HTTPMethod 5 | @_exported import struct NIOHTTP1.HTTPHeaders 6 | @_exported import class AsyncHTTPClient.HTTPClient 7 | -------------------------------------------------------------------------------- /Sources/GitHubKit/GitHub.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import NIO 3 | import NIOHTTP1 4 | import AsyncHTTPClient 5 | 6 | 7 | /// Main GitHub service class 8 | public class GitHub: GitHubClient { 9 | 10 | public enum Error: Swift.Error { 11 | 12 | case notFound(String) 13 | 14 | case invalidContent 15 | 16 | case missingRediretHeaders 17 | 18 | } 19 | 20 | /// Main configuration 21 | public struct Config { 22 | 23 | /// GitHub username associated with a personal access token 24 | public let username: String 25 | 26 | /// Personal access token 27 | public let token: String 28 | 29 | /// Server URL 30 | public let server: GitHubServerConvertible 31 | 32 | /// Initializer 33 | public init(username: String, token: String, server: GitHubServerConvertible = GitHubServer.github) { 34 | self.username = username 35 | self.token = token 36 | self.server = server 37 | } 38 | 39 | } 40 | 41 | /// Format of an archive 42 | public enum Format: String { 43 | case tarball 44 | case zipball 45 | } 46 | 47 | /// Copy of the given configuration 48 | public let config: Config 49 | 50 | let client: AsyncHTTPClient.HTTPClient 51 | 52 | /// Initializer 53 | public init(_ config: Config, eventLoopGroupProvider provider: AsyncHTTPClient.HTTPClient.EventLoopGroupProvider = .createNew, proxy: AsyncHTTPClient.HTTPClient.Configuration.Proxy? = nil) throws { 54 | self.config = config 55 | var conf = AsyncHTTPClient.HTTPClient.Configuration() 56 | conf.proxy = proxy 57 | self.client = AsyncHTTPClient.HTTPClient( 58 | eventLoopGroupProvider: provider, 59 | configuration: conf 60 | ) 61 | } 62 | 63 | /// Initializer 64 | public init(_ config: Config, eventLoop: EventLoop) throws { 65 | self.config = config 66 | self.client = AsyncHTTPClient.HTTPClient(eventLoopGroupProvider: .shared(eventLoop)) 67 | } 68 | 69 | var eventLoop: EventLoop { 70 | return client.eventLoopGroup.next() 71 | } 72 | 73 | public func syncShutdown() throws { 74 | try client.syncShutdown() 75 | } 76 | 77 | } 78 | 79 | 80 | extension AsyncHTTPClient.HTTPClient.Response { 81 | 82 | mutating func data() -> Data? { 83 | guard var byteBuffer = body else { 84 | return nil 85 | } 86 | guard let data = byteBuffer.readBytes(length: byteBuffer.readableBytes) else { 87 | return nil 88 | } 89 | return Data(data) 90 | } 91 | 92 | } 93 | 94 | 95 | extension GitHub { 96 | 97 | fileprivate func req(_ method: HTTPMethod, _ path: String, _ body: AsyncHTTPClient.HTTPClient.Body? = nil) throws -> AsyncHTTPClient.HTTPClient.Request { 98 | let url = config.url(for: path) 99 | let req = try AsyncHTTPClient.HTTPClient.Request( 100 | url: url, 101 | method: method, 102 | headers: headers, 103 | body: body 104 | ) 105 | return req 106 | } 107 | 108 | /// Retrieve data from GitHub API and turn them into a model 109 | public func get(path: String) throws -> EventLoopFuture where C: Decodable { 110 | let r = try req(.GET, path) 111 | let future = client.execute(request: r) 112 | return future.flatMap() { response in 113 | var response = response 114 | guard response.status == .ok else { 115 | var r = response 116 | if let data = r.body?.readString(length: response.body!.readableBytes) { 117 | print(data) 118 | } 119 | return self.eventLoop.makeFailedFuture(Error.notFound(path)) 120 | } 121 | do { 122 | guard let data = response.data() else { 123 | return self.eventLoop.makeFailedFuture(Error.invalidContent) 124 | } 125 | let decoded = try JSONDecoder().decode(C.self, from: data) 126 | return self.eventLoop.makeSucceededFuture(decoded) 127 | } catch { 128 | return self.eventLoop.makeFailedFuture(error) 129 | } 130 | } 131 | } 132 | 133 | /// Post 134 | public func post(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable { 135 | return try send(method: .POST, path: path, post: post) 136 | } 137 | 138 | /// Put 139 | public func put(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable { 140 | return try send(method: .PUT, path: path, post: post) 141 | } 142 | 143 | /// Patch 144 | public func patch(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable { 145 | return try send(method: .PATCH, path: path, post: post) 146 | } 147 | 148 | /// Delete 149 | public func delete(path: String) throws -> EventLoopFuture { 150 | let r = try req(.GET, path) 151 | let future = client.execute(request: r) 152 | return future.flatMap() { response in 153 | guard response.status == .ok || response.status == .noContent else { 154 | return self.eventLoop.makeFailedFuture(Error.notFound(path)) 155 | } 156 | return self.eventLoop.makeSucceededFuture(Void()) 157 | } 158 | } 159 | 160 | /// Retrieve a file 161 | public func get(file path: String) throws -> EventLoopFuture { 162 | let r = try req(.GET, path) 163 | let future = client.execute(request: r) 164 | return future.flatMap() { response in 165 | var response = response 166 | guard response.status == .ok || response.status == .noContent else { 167 | return self.eventLoop.makeFailedFuture(Error.notFound(path)) 168 | } 169 | return self.eventLoop.makeSucceededFuture(response.data()) 170 | } 171 | } 172 | 173 | /// Get a redirect link 174 | public func redirect(file path: String, status: HTTPResponseStatus = .found) throws -> EventLoopFuture { 175 | let r = try req(.GET, path) 176 | let future = client.execute(request: r) 177 | return future.flatMap() { response in 178 | guard response.status == status else { 179 | return self.eventLoop.makeFailedFuture(Error.notFound(path)) 180 | } 181 | guard response.headers.contains(name: "Location"), let redirect = response.headers["Location"].first else { 182 | return self.eventLoop.makeFailedFuture(Error.missingRediretHeaders) 183 | } 184 | return self.eventLoop.makeSucceededFuture(redirect) 185 | } 186 | } 187 | 188 | /// Get a temporary download link for an archive 189 | public func download(org: String, repo: String, ref: String, format: Format = .tarball) throws -> EventLoopFuture { 190 | return try redirect(file: "repos/\(org)/\(repo)/\(format.rawValue)/\(ref)", status: .found) 191 | } 192 | 193 | } 194 | 195 | 196 | extension GitHub { 197 | 198 | /// Auth headers for request 199 | private var headers: HTTPHeaders { 200 | let loginString = "\(config.username):\(config.token)" 201 | let loginData = loginString.data(using: String.Encoding.utf8)! 202 | let base64LoginString = loginData.base64EncodedString() 203 | 204 | let headers: HTTPHeaders = [ 205 | "Authorization": "Basic \(base64LoginString)", 206 | "User-Agent": "GitHubKit(https://github.com/Einstore/GitHubKit)" 207 | ] 208 | return headers 209 | } 210 | 211 | /// Send a request 212 | private func send(method: HTTPMethod, path: String, post: E? = nil) throws -> EventLoopFuture where C: Decodable, E: Encodable { 213 | let body: AsyncHTTPClient.HTTPClient.Body? 214 | if let post = post { 215 | let jsonData = try JSONEncoder().encode(post) 216 | body = .data(jsonData) 217 | } else { 218 | body = nil 219 | } 220 | 221 | let r = try req(method, path, body) 222 | let future = client.execute(request: r) 223 | return future.flatMap() { response in 224 | var response = response 225 | guard response.status == .ok || response.status == .created else { 226 | if let data = response.data() { 227 | print("Error data: " + (String(data: data, encoding: .utf8) ?? "No error data to print")) 228 | } 229 | return self.eventLoop.makeFailedFuture(Error.notFound(path)) 230 | } 231 | if response.body?.capacity == 0 { 232 | return self.eventLoop.makeSucceededFuture(nil) 233 | } 234 | do { 235 | guard let data = response.data() else { 236 | return self.eventLoop.makeFailedFuture(Error.invalidContent) 237 | } 238 | let decoded = try JSONDecoder().decode(C.self, from: data) 239 | return self.eventLoop.makeSucceededFuture(decoded) 240 | } catch { 241 | return self.eventLoop.makeFailedFuture(error) 242 | } 243 | } 244 | } 245 | 246 | } 247 | 248 | 249 | extension String: GitHubServerConvertible { 250 | 251 | /// Self value of a string 252 | public var value: String { 253 | return self 254 | } 255 | 256 | } 257 | 258 | 259 | extension GitHub.Config { 260 | 261 | /// Build URL from a path 262 | func url(for path: String) -> String { 263 | return server.value 264 | .trimmingCharacters(in: .init(charactersIn: "/")) 265 | .appending("/") 266 | .appending(path) 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /Sources/GitHubKit/GitHubClient.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import NIOHTTP1 3 | 4 | 5 | public protocol GitHubClient { 6 | 7 | func syncShutdown() throws 8 | func get(path: String) throws -> EventLoopFuture where C: Decodable 9 | func post(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable 10 | func put(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable 11 | func patch(path: String, post: E) throws -> EventLoopFuture where C: Decodable, E: Encodable 12 | func delete(path: String) throws -> EventLoopFuture 13 | func get(file path: String) throws -> EventLoopFuture 14 | func redirect(file path: String, status: HTTPResponseStatus) throws -> EventLoopFuture 15 | func download(org: String, repo: String, ref: String, format: GitHub.Format) throws -> EventLoopFuture 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Sources/GitHubKit/GitHubServer.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | /// Server value convertible 5 | public protocol GitHubServerConvertible { 6 | var value: String { get } 7 | } 8 | 9 | /// Predefined github server values 10 | public enum GitHubServer: GitHubServerConvertible { 11 | 12 | /// api.github.com 13 | case github 14 | 15 | /// Enterprise GitHub 16 | case enterprise(String) 17 | 18 | /// Server URL 19 | public var value: String { 20 | switch self { 21 | case .github: 22 | return "https://api.github.com" 23 | case .enterprise(let url): 24 | return url 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Author.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Author: Codable { 5 | 6 | public internal(set) var name: String? 7 | public internal(set) var date: String? 8 | public internal(set) var email: String? 9 | 10 | init(name: String?, date: String?, email: String?) { 11 | self.name = name 12 | self.date = date 13 | self.email = email 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Branch.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Branch: Codable { 5 | 6 | public struct Links: Codable { 7 | 8 | public internal(set) var html: String? 9 | public internal(set) var linksSelf: String? 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case html = "html" 13 | case linksSelf = "self" 14 | } 15 | 16 | init(html: String? = nil, linksSelf: String? = nil) { 17 | self.html = html 18 | self.linksSelf = linksSelf 19 | } 20 | 21 | } 22 | 23 | public struct Protection: Codable { 24 | 25 | public internal(set) var enabled: Bool? 26 | public internal(set) var requiredStatusChecks: RequiredStatusChecks? 27 | 28 | enum CodingKeys: String, CodingKey { 29 | case enabled = "enabled" 30 | case requiredStatusChecks = "required_status_checks" 31 | } 32 | 33 | init(enabled: Bool? = nil, requiredStatusChecks: RequiredStatusChecks? = nil) { 34 | self.enabled = enabled 35 | self.requiredStatusChecks = requiredStatusChecks 36 | } 37 | 38 | } 39 | 40 | public struct RequiredStatusChecks: Codable { 41 | 42 | public internal(set) var enforcementLevel: String? 43 | public internal(set) var contexts: [String]? 44 | 45 | enum CodingKeys: String, CodingKey { 46 | case enforcementLevel = "enforcement_level" 47 | case contexts = "contexts" 48 | } 49 | 50 | init(enforcementLevel: String? = nil, contexts: [String]? = nil) { 51 | self.enforcementLevel = enforcementLevel 52 | self.contexts = contexts 53 | } 54 | 55 | } 56 | 57 | public internal(set) var name: String 58 | public internal(set) var commit: Commit 59 | public internal(set) var links: Links? 60 | public internal(set) var protected: Bool 61 | public internal(set) var protection: Protection? 62 | public internal(set) var protectionURL: String? 63 | 64 | enum CodingKeys: String, CodingKey { 65 | case name = "name" 66 | case commit = "commit" 67 | case links = "_links" 68 | case protected = "protected" 69 | case protection = "protection" 70 | case protectionURL = "protection_url" 71 | } 72 | 73 | init(name: String, commit: Commit, links: Links? = nil, protected: Bool = false, protection: Protection? = nil, protectionURL: String? = nil) { 74 | self.name = name 75 | self.commit = commit 76 | self.links = links 77 | self.protected = protected 78 | self.protection = protection 79 | self.protectionURL = protectionURL 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Comment.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Comment: Codable { 5 | 6 | public struct Post: Codable { 7 | public internal(set) var body: String 8 | } 9 | 10 | public internal(set) var id: Int 11 | public internal(set) var nodeID: String 12 | public internal(set) var url: String? 13 | public internal(set) var htmlURL: String? 14 | public internal(set) var body: String 15 | public internal(set) var user: Owner 16 | public internal(set) var createdAt: String 17 | public internal(set) var updatedAt: String? 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case id 21 | case nodeID = "node_id" 22 | case url 23 | case htmlURL = "html_url" 24 | case body 25 | case user 26 | case createdAt = "created_at" 27 | case updatedAt = "updated_at" 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Commit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Commit: Codable { 5 | 6 | public struct Tree: Codable { 7 | public internal(set) var sha: String? 8 | public internal(set) var url: String? 9 | } 10 | 11 | public struct Author: Codable { 12 | 13 | public internal(set) var gravatarID: String? 14 | public internal(set) var avatarURL: String? 15 | public internal(set) var url: String? 16 | public internal(set) var id: Int? 17 | public internal(set) var login: String? 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case gravatarID = "gravatar_id" 21 | case avatarURL = "avatar_url" 22 | case url = "url" 23 | case id = "id" 24 | case login = "login" 25 | } 26 | 27 | } 28 | 29 | public struct Info: Codable { 30 | 31 | public internal(set) var author: Author? 32 | public internal(set) var url: String? 33 | public internal(set) var message: String? 34 | public internal(set) var tree: Tree? 35 | public internal(set) var committer: Author? 36 | public internal(set) var verification: Verification? 37 | } 38 | 39 | 40 | public internal(set) var sha: String 41 | public internal(set) var nodeID: String? 42 | public internal(set) var commit: Info? 43 | public internal(set) var author: Author? 44 | public internal(set) var parents: [Tree]? 45 | public internal(set) var url: String 46 | public internal(set) var committer: Author? 47 | 48 | enum CodingKeys: String, CodingKey { 49 | case sha = "sha" 50 | case nodeID = "node_id" 51 | case commit = "commit" 52 | case author = "author" 53 | case parents = "parents" 54 | case url = "url" 55 | case committer = "committer" 56 | } 57 | 58 | init(sha: String, nodeID: String? = nil, commit: Info? = nil, author: Author? = nil, parents: [Tree]? = nil, url: String, committer: Author? = nil) { 59 | self.sha = sha 60 | self.nodeID = nodeID 61 | self.commit = commit 62 | self.author = author 63 | self.parents = parents 64 | self.url = url 65 | self.committer = committer 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/File.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct File: Codable { 5 | 6 | public enum Error: Swift.Error { 7 | case missingDownloadUrl 8 | case notFile(String) 9 | } 10 | 11 | public struct Links: Codable { 12 | public internal(set) var git: String 13 | public internal(set) var linkSelf: String 14 | public internal(set) var html: String 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case git = "git" 18 | case linkSelf = "self" 19 | case html = "html" 20 | } 21 | 22 | } 23 | 24 | public internal(set) var type: String 25 | public internal(set) var encoding: String 26 | public internal(set) var size: Int 27 | public internal(set) var name: String 28 | public internal(set) var path: String 29 | public internal(set) var content: String? 30 | public internal(set) var sha: String 31 | public internal(set) var target: String? 32 | public internal(set) var submoduleGitUrl: String? 33 | public internal(set) var url: String 34 | public internal(set) var gitURL: String 35 | public internal(set) var htmlURL: String 36 | public internal(set) var downloadURL: String? 37 | public internal(set) var links: Links 38 | 39 | enum CodingKeys: String, CodingKey { 40 | case type = "type" 41 | case encoding = "encoding" 42 | case size = "size" 43 | case name = "name" 44 | case path = "path" 45 | case content = "content" 46 | case sha = "sha" 47 | case target = "target" 48 | case submoduleGitUrl = "submodule_git_url" 49 | case url = "url" 50 | case gitURL = "git_url" 51 | case htmlURL = "html_url" 52 | case downloadURL = "download_url" 53 | case links = "_links" 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Git/GitBlob.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct GitBlob: Codable { 5 | 6 | public internal(set) var content: String 7 | public internal(set) var encoding: String 8 | public internal(set) var url: String? 9 | public internal(set) var sha: String 10 | public internal(set) var size: Int? 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Git/GitCommit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct GitCommit: Codable { 5 | 6 | public struct Tree: Codable { 7 | 8 | public internal(set) var url: String 9 | public internal(set) var sha: String 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case url = "url" 13 | case sha = "sha" 14 | } 15 | 16 | } 17 | 18 | public internal(set) var sha: String 19 | public internal(set) var url: String? 20 | public internal(set) var author: Author? 21 | public internal(set) var committer: Author? 22 | public internal(set) var message: String? 23 | public internal(set) var tree: Tree 24 | public internal(set) var parents: [Tree]? 25 | public internal(set) var verification: Verification? 26 | 27 | enum CodingKeys: String, CodingKey { 28 | case sha = "sha" 29 | case url = "url" 30 | case author = "author" 31 | case committer = "committer" 32 | case message = "message" 33 | case tree = "tree" 34 | case parents = "parents" 35 | case verification = "verification" 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Git/GitTree.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct GitTree: Codable { 5 | 6 | public struct Object: Codable { 7 | 8 | public internal(set) var path: String 9 | public internal(set) var mode: String 10 | public internal(set) var type: String 11 | public internal(set) var size: Int? 12 | public internal(set) var sha: String 13 | public internal(set) var url: String? 14 | 15 | } 16 | 17 | public internal(set) var sha: String 18 | public internal(set) var url: String? 19 | public internal(set) var objects: [Object]? 20 | public internal(set) var truncated: Bool 21 | 22 | enum CodingKeys: String, CodingKey { 23 | case sha 24 | case url 25 | case objects = "tree" 26 | case truncated 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/License.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct License: Codable { 4 | 5 | public internal(set) var key: String? 6 | public internal(set) var name: String? 7 | public internal(set) var spdxID: String? 8 | public internal(set) var url: String? 9 | public internal(set) var nodeID: String? 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case key = "key" 13 | case name = "name" 14 | case spdxID = "spdx_id" 15 | case url = "url" 16 | case nodeID = "node_id" 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Milestone.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Milestone: Codable { 5 | 6 | public internal(set) var url: String? 7 | public internal(set) var htmlURL: String? 8 | public internal(set) var labelsURL: String? 9 | public internal(set) var id: Int? 10 | public internal(set) var nodeID: String? 11 | public internal(set) var number: Int? 12 | public internal(set) var state: String? 13 | public internal(set) var title: String? 14 | public internal(set) var milestoneDescription: String? 15 | public internal(set) var creator: Owner? 16 | public internal(set) var openIssues: Int? 17 | public internal(set) var closedIssues: Int? 18 | public internal(set) var createdAt: String? 19 | public internal(set) var updatedAt: String? 20 | public internal(set) var closedAt: String? 21 | public internal(set) var dueOn: String? 22 | 23 | enum CodingKeys: String, CodingKey { 24 | case url = "url" 25 | case htmlURL = "html_url" 26 | case labelsURL = "labels_url" 27 | case id = "id" 28 | case nodeID = "node_id" 29 | case number = "number" 30 | case state = "state" 31 | case title = "title" 32 | case milestoneDescription = "description" 33 | case creator = "creator" 34 | case openIssues = "open_issues" 35 | case closedIssues = "closed_issues" 36 | case createdAt = "created_at" 37 | case updatedAt = "updated_at" 38 | case closedAt = "closed_at" 39 | case dueOn = "due_on" 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Organization.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | /// Organization 5 | public struct Organization: Codable { 6 | 7 | public struct Plan: Codable { 8 | 9 | public internal(set) var name: String? 10 | public internal(set) var space: Int? 11 | public internal(set) var privateRepos: Int? 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case name = "name" 15 | case space = "space" 16 | case privateRepos = "private_repos" 17 | } 18 | 19 | } 20 | 21 | public internal(set) var login: String 22 | public internal(set) var id: Int 23 | public internal(set) var nodeID: String? 24 | public internal(set) var url: String? 25 | public internal(set) var reposURL: String? 26 | public internal(set) var eventsURL: String? 27 | public internal(set) var hooksURL: String? 28 | public internal(set) var issuesURL: String? 29 | public internal(set) var membersURL: String? 30 | public internal(set) var publicMembersURL: String? 31 | public internal(set) var avatarURL: String? 32 | public internal(set) var organizationDescription: String? 33 | public internal(set) var name: String? 34 | public internal(set) var company: String? 35 | public internal(set) var blog: String? 36 | public internal(set) var location: String? 37 | public internal(set) var email: String? 38 | public internal(set) var isVerified: Bool? 39 | public internal(set) var hasOrganizationProjects: Bool? 40 | public internal(set) var hasRepositoryProjects: Bool? 41 | public internal(set) var publicRepos: Int? 42 | public internal(set) var publicGists: Int? 43 | public internal(set) var followers: Int? 44 | public internal(set) var following: Int? 45 | public internal(set) var htmlURL: String? 46 | public internal(set) var createdAt: String? 47 | public internal(set) var type: String? 48 | public internal(set) var totalPrivateRepos: Int? 49 | public internal(set) var ownedPrivateRepos: Int? 50 | public internal(set) var privateGists: Int? 51 | public internal(set) var diskUsage: Int? 52 | public internal(set) var collaborators: Int? 53 | public internal(set) var billingEmail: String? 54 | public internal(set) var plan: Plan? 55 | public internal(set) var defaultRepositorySettings: String? 56 | public internal(set) var membersCanCreateRepositories: Bool? 57 | public internal(set) var twoFactorRequirementEnabled: Bool? 58 | public internal(set) var membersAllowedRepositoryCreationType: String? 59 | 60 | enum CodingKeys: String, CodingKey { 61 | case login = "login" 62 | case id = "id" 63 | case nodeID = "node_id" 64 | case url = "url" 65 | case reposURL = "repos_url" 66 | case eventsURL = "events_url" 67 | case hooksURL = "hooks_url" 68 | case issuesURL = "issues_url" 69 | case membersURL = "members_url" 70 | case publicMembersURL = "public_members_url" 71 | case avatarURL = "avatar_url" 72 | case organizationDescription = "description" 73 | case name = "name" 74 | case company = "company" 75 | case blog = "blog" 76 | case location = "location" 77 | case email = "email" 78 | case isVerified = "is_verified" 79 | case hasOrganizationProjects = "has_organization_projects" 80 | case hasRepositoryProjects = "has_repository_projects" 81 | case publicRepos = "public_repos" 82 | case publicGists = "public_gists" 83 | case followers = "followers" 84 | case following = "following" 85 | case htmlURL = "html_url" 86 | case createdAt = "created_at" 87 | case type = "type" 88 | case totalPrivateRepos = "total_private_repos" 89 | case ownedPrivateRepos = "owned_private_repos" 90 | case privateGists = "private_gists" 91 | case diskUsage = "disk_usage" 92 | case collaborators = "collaborators" 93 | case billingEmail = "billing_email" 94 | case plan = "plan" 95 | case defaultRepositorySettings = "default_repository_settings" 96 | case membersCanCreateRepositories = "members_can_create_repositories" 97 | case twoFactorRequirementEnabled = "two_factor_requirement_enabled" 98 | case membersAllowedRepositoryCreationType = "members_allowed_repository_creation_type" 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Owner.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Owner: Codable { 5 | 6 | public internal(set) var login: String 7 | public internal(set) var id: Int 8 | public internal(set) var nodeID: String? 9 | public internal(set) var avatarURL: String? 10 | public internal(set) var gravatarID: String? 11 | public internal(set) var url: String? 12 | public internal(set) var htmlURL: String? 13 | public internal(set) var followersURL: String? 14 | public internal(set) var followingURL: String? 15 | public internal(set) var gistsURL: String? 16 | public internal(set) var starredURL: String? 17 | public internal(set) var subscriptionsURL: String? 18 | public internal(set) var organizationsURL: String? 19 | public internal(set) var reposURL: String? 20 | public internal(set) var eventsURL: String? 21 | public internal(set) var receivedEventsURL: String? 22 | public internal(set) var type: String? 23 | public internal(set) var siteAdmin: Bool? 24 | 25 | enum CodingKeys: String, CodingKey { 26 | case login = "login" 27 | case id = "id" 28 | case nodeID = "node_id" 29 | case avatarURL = "avatar_url" 30 | case gravatarID = "gravatar_id" 31 | case url = "url" 32 | case htmlURL = "html_url" 33 | case followersURL = "followers_url" 34 | case followingURL = "following_url" 35 | case gistsURL = "gists_url" 36 | case starredURL = "starred_url" 37 | case subscriptionsURL = "subscriptions_url" 38 | case organizationsURL = "organizations_url" 39 | case reposURL = "repos_url" 40 | case eventsURL = "events_url" 41 | case receivedEventsURL = "received_events_url" 42 | case type = "type" 43 | case siteAdmin = "site_admin" 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/PR.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct PR: Codable { 5 | 6 | public struct Location: Codable { 7 | 8 | public internal(set) var label: String? 9 | public internal(set) var ref: String? 10 | public internal(set) var sha: String? 11 | public internal(set) var user: Owner? 12 | public internal(set) var repo: Repo? 13 | 14 | } 15 | 16 | public struct Label: Codable { 17 | 18 | public internal(set) var id: Int? 19 | public internal(set) var nodeID: String? 20 | public internal(set) var url: String? 21 | public internal(set) var name: String? 22 | public internal(set) var labelDescription: String? 23 | public internal(set) var color: String? 24 | public internal(set) var labelDefault: Bool? 25 | 26 | enum CodingKeys: String, CodingKey { 27 | case id = "id" 28 | case nodeID = "node_id" 29 | case url = "url" 30 | case name = "name" 31 | case labelDescription = "description" 32 | case color = "color" 33 | case labelDefault = "default" 34 | } 35 | 36 | } 37 | 38 | 39 | public struct RequestedTeam: Codable { 40 | 41 | public internal(set) var id: Int? 42 | public internal(set) var nodeID: String? 43 | public internal(set) var url: String? 44 | public internal(set) var name: String? 45 | public internal(set) var slug: String? 46 | public internal(set) var requestedTeamDescription: String? 47 | public internal(set) var privacy: String? 48 | public internal(set) var permission: String? 49 | public internal(set) var membersURL: String? 50 | public internal(set) var repositoriesURL: String? 51 | 52 | enum CodingKeys: String, CodingKey { 53 | case id = "id" 54 | case nodeID = "node_id" 55 | case url = "url" 56 | case name = "name" 57 | case slug = "slug" 58 | case requestedTeamDescription = "description" 59 | case privacy = "privacy" 60 | case permission = "permission" 61 | case membersURL = "members_url" 62 | case repositoriesURL = "repositories_url" 63 | } 64 | 65 | } 66 | 67 | public struct Links: Codable { 68 | 69 | public struct Href: Codable { 70 | 71 | public internal(set) var href: String? 72 | 73 | } 74 | 75 | public internal(set) var linksSelf: Href? 76 | public internal(set) var html: Href? 77 | public internal(set) var issue: Href? 78 | public internal(set) var comments: Href? 79 | public internal(set) var reviewComments: Href? 80 | public internal(set) var reviewComment: Href? 81 | public internal(set) var commits: Href? 82 | public internal(set) var statuses: Href? 83 | 84 | enum CodingKeys: String, CodingKey { 85 | case linksSelf = "self" 86 | case html = "html" 87 | case issue = "issue" 88 | case comments = "comments" 89 | case reviewComments = "review_comments" 90 | case reviewComment = "review_comment" 91 | case commits = "commits" 92 | case statuses = "statuses" 93 | } 94 | 95 | } 96 | 97 | public internal(set) var url: String 98 | public internal(set) var id: Int 99 | public internal(set) var nodeID: String 100 | public internal(set) var htmlURL: String 101 | public internal(set) var diffURL: String? 102 | public internal(set) var patchURL: String? 103 | public internal(set) var issueURL: String? 104 | public internal(set) var commitsURL: String? 105 | public internal(set) var reviewCommentsURL: String? 106 | public internal(set) var reviewCommentURL: String? 107 | public internal(set) var commentsURL: String? 108 | public internal(set) var statusesURL: String? 109 | public internal(set) var number: Int 110 | public internal(set) var state: String 111 | public internal(set) var locked: Bool? 112 | public internal(set) var title: String 113 | public internal(set) var user: Owner? 114 | public internal(set) var body: String? 115 | public internal(set) var labels: [Label]? 116 | public internal(set) var milestone: Milestone? 117 | public internal(set) var activeLockReason: String? 118 | public internal(set) var createdAt: String 119 | public internal(set) var updatedAt: String? 120 | public internal(set) var closedAt: String? 121 | public internal(set) var mergedAt: String? 122 | public internal(set) var mergeCommitSHA: String? 123 | public internal(set) var assignee: Owner? 124 | public internal(set) var assignees: [Owner]? 125 | public internal(set) var requestedReviewers: [Owner]? 126 | public internal(set) var requestedTeams: [RequestedTeam]? 127 | public internal(set) var head: Location? 128 | public internal(set) var base: Location? 129 | public internal(set) var links: Links? 130 | public internal(set) var authorAssociation: String? 131 | public internal(set) var draft: Bool? 132 | public internal(set) var merged: Bool? 133 | public internal(set) var mergeable: Bool? 134 | public internal(set) var rebaseable: Bool? 135 | public internal(set) var mergeableState: String? 136 | public internal(set) var mergedBy: Owner? 137 | public internal(set) var comments: Int? 138 | public internal(set) var reviewComments: Int? 139 | public internal(set) var maintainerCanModify: Bool? 140 | public internal(set) var commits: Int? 141 | public internal(set) var additions: Int? 142 | public internal(set) var deletions: Int? 143 | public internal(set) var changedFiles: Int? 144 | 145 | enum CodingKeys: String, CodingKey { 146 | case url = "url" 147 | case id = "id" 148 | case nodeID = "node_id" 149 | case htmlURL = "html_url" 150 | case diffURL = "diff_url" 151 | case patchURL = "patch_url" 152 | case issueURL = "issue_url" 153 | case commitsURL = "commits_url" 154 | case reviewCommentsURL = "review_comments_url" 155 | case reviewCommentURL = "review_comment_url" 156 | case commentsURL = "comments_url" 157 | case statusesURL = "statuses_url" 158 | case number = "number" 159 | case state = "state" 160 | case locked = "locked" 161 | case title = "title" 162 | case user = "user" 163 | case body = "body" 164 | case labels = "labels" 165 | case milestone = "milestone" 166 | case activeLockReason = "active_lock_reason" 167 | case createdAt = "created_at" 168 | case updatedAt = "updated_at" 169 | case closedAt = "closed_at" 170 | case mergedAt = "merged_at" 171 | case mergeCommitSHA = "merge_commit_sha" 172 | case assignee = "assignee" 173 | case assignees = "assignees" 174 | case requestedReviewers = "requested_reviewers" 175 | case requestedTeams = "requested_teams" 176 | case head = "head" 177 | case base = "base" 178 | case links = "_links" 179 | case authorAssociation = "author_association" 180 | case draft = "draft" 181 | case merged = "merged" 182 | case mergeable = "mergeable" 183 | case rebaseable = "rebaseable" 184 | case mergeableState = "mergeable_state" 185 | case mergedBy = "merged_by" 186 | case comments = "comments" 187 | case reviewComments = "review_comments" 188 | case maintainerCanModify = "maintainer_can_modify" 189 | case commits = "commits" 190 | case additions = "additions" 191 | case deletions = "deletions" 192 | case changedFiles = "changed_files" 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Release.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Release: Codable { 5 | 6 | public struct Asset: Codable { 7 | 8 | public internal(set) var url: String? 9 | public internal(set) var browserDownloadURL: String? 10 | public internal(set) var id: Int 11 | public internal(set) var nodeID: String? 12 | public internal(set) var name: String? 13 | public internal(set) var label: String? 14 | public internal(set) var state: String? 15 | public internal(set) var contentType: String 16 | public internal(set) var size: Int 17 | public internal(set) var downloadCount: Int? 18 | public internal(set) var createdAt: String? 19 | public internal(set) var updatedAt: String? 20 | public internal(set) var uploader: Owner? 21 | 22 | enum CodingKeys: String, CodingKey { 23 | case url = "url" 24 | case browserDownloadURL = "browser_download_url" 25 | case id = "id" 26 | case nodeID = "node_id" 27 | case name = "name" 28 | case label = "label" 29 | case state = "state" 30 | case contentType = "content_type" 31 | case size = "size" 32 | case downloadCount = "download_count" 33 | case createdAt = "created_at" 34 | case updatedAt = "updated_at" 35 | case uploader = "uploader" 36 | } 37 | 38 | } 39 | 40 | public internal(set) var url: String? 41 | public internal(set) var htmlURL: String? 42 | public internal(set) var assetsURL: String? 43 | public internal(set) var uploadURL: String? 44 | public internal(set) var tarballURL: String? 45 | public internal(set) var zipballURL: String? 46 | public internal(set) var id: Int 47 | public internal(set) var nodeID: String? 48 | public internal(set) var tagName: String 49 | public internal(set) var targetCommitish: String? 50 | public internal(set) var name: String 51 | public internal(set) var body: String? 52 | public internal(set) var draft: Bool 53 | public internal(set) var prerelease: Bool 54 | public internal(set) var createdAt: String? 55 | public internal(set) var publishedAt: String? 56 | public internal(set) var author: Owner? 57 | public internal(set) var assets: [Asset]? 58 | 59 | enum CodingKeys: String, CodingKey { 60 | case url = "url" 61 | case htmlURL = "html_url" 62 | case assetsURL = "assets_url" 63 | case uploadURL = "upload_url" 64 | case tarballURL = "tarball_url" 65 | case zipballURL = "zipball_url" 66 | case id = "id" 67 | case nodeID = "node_id" 68 | case tagName = "tag_name" 69 | case targetCommitish = "target_commitish" 70 | case name = "name" 71 | case body = "body" 72 | case draft = "draft" 73 | case prerelease = "prerelease" 74 | case createdAt = "created_at" 75 | case publishedAt = "published_at" 76 | case author = "author" 77 | case assets = "assets" 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Repo.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Repo: Codable { 5 | 6 | public struct Permissions: Codable { 7 | 8 | public internal(set) var admin: Bool 9 | public internal(set) var push: Bool 10 | public internal(set) var pull: Bool 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case admin = "admin" 14 | case push = "push" 15 | case pull = "pull" 16 | } 17 | 18 | } 19 | 20 | public internal(set) var id: Int 21 | public internal(set) var nodeID: String? 22 | public internal(set) var name: String 23 | public internal(set) var fullName: String 24 | public internal(set) var repoPrivate: Bool 25 | public internal(set) var owner: Owner 26 | public internal(set) var htmlURL: String 27 | public internal(set) var repoDescription: String? 28 | public internal(set) var fork: Bool 29 | public internal(set) var url: String? 30 | public internal(set) var forksURL: String? 31 | public internal(set) var keysURL: String? 32 | public internal(set) var collaboratorsURL: String? 33 | public internal(set) var teamsURL: String? 34 | public internal(set) var hooksURL: String? 35 | public internal(set) var issueEventsURL: String? 36 | public internal(set) var eventsURL: String? 37 | public internal(set) var assigneesURL: String? 38 | public internal(set) var branchesURL: String? 39 | public internal(set) var tagsURL: String? 40 | public internal(set) var blobsURL: String? 41 | public internal(set) var gitTagsURL: String? 42 | public internal(set) var gitRefsURL: String? 43 | public internal(set) var treesURL: String? 44 | public internal(set) var statusesURL: String? 45 | public internal(set) var languagesURL: String? 46 | public internal(set) var stargazersURL: String? 47 | public internal(set) var contributorsURL: String? 48 | public internal(set) var subscribersURL: String? 49 | public internal(set) var subscriptionURL: String? 50 | public internal(set) var commitsURL: String? 51 | public internal(set) var gitCommitsURL: String? 52 | public internal(set) var commentsURL: String? 53 | public internal(set) var issueCommentURL: String? 54 | public internal(set) var contentsURL: String? 55 | public internal(set) var compareURL: String? 56 | public internal(set) var mergesURL: String? 57 | public internal(set) var archiveURL: String? 58 | public internal(set) var downloadsURL: String? 59 | public internal(set) var issuesURL: String? 60 | public internal(set) var pullsURL: String? 61 | public internal(set) var milestonesURL: String? 62 | public internal(set) var notificationsURL: String? 63 | public internal(set) var labelsURL: String? 64 | public internal(set) var releasesURL: String? 65 | public internal(set) var deploymentsURL: String? 66 | public internal(set) var createdAt: String 67 | public internal(set) var updatedAt: String? 68 | public internal(set) var pushedAt: String? 69 | public internal(set) var gitURL: String? 70 | public internal(set) var sshURL: String? 71 | public internal(set) var cloneURL: String? 72 | public internal(set) var svnURL: String? 73 | public internal(set) var homepage: String? 74 | public internal(set) var size: Int? 75 | public internal(set) var stargazersCount: Int? 76 | public internal(set) var watchersCount: Int? 77 | public internal(set) var language: String? 78 | public internal(set) var hasIssues: Bool? 79 | public internal(set) var hasProjects: Bool? 80 | public internal(set) var hasDownloads: Bool? 81 | public internal(set) var hasWiki: Bool? 82 | public internal(set) var hasPages: Bool? 83 | public internal(set) var forksCount: Int? 84 | public internal(set) var mirrorURL: String? 85 | public internal(set) var archived: Bool? 86 | public internal(set) var disabled: Bool? 87 | public internal(set) var openIssuesCount: Int? 88 | public internal(set) var license: License? 89 | public internal(set) var forks: Int? 90 | public internal(set) var openIssues: Int? 91 | public internal(set) var watchers: Int? 92 | public internal(set) var defaultBranch: String? 93 | public internal(set) var permissions: Permissions? 94 | public internal(set) var topics: [String]? 95 | 96 | enum CodingKeys: String, CodingKey { 97 | case id = "id" 98 | case nodeID = "node_id" 99 | case name = "name" 100 | case fullName = "full_name" 101 | case repoPrivate = "private" 102 | case owner = "owner" 103 | case htmlURL = "html_url" 104 | case repoDescription = "description" 105 | case fork = "fork" 106 | case url = "url" 107 | case forksURL = "forks_url" 108 | case keysURL = "keys_url" 109 | case collaboratorsURL = "collaborators_url" 110 | case teamsURL = "teams_url" 111 | case hooksURL = "hooks_url" 112 | case issueEventsURL = "issue_events_url" 113 | case eventsURL = "events_url" 114 | case assigneesURL = "assignees_url" 115 | case branchesURL = "branches_url" 116 | case tagsURL = "tags_url" 117 | case blobsURL = "blobs_url" 118 | case gitTagsURL = "git_tags_url" 119 | case gitRefsURL = "git_refs_url" 120 | case treesURL = "trees_url" 121 | case statusesURL = "statuses_url" 122 | case languagesURL = "languages_url" 123 | case stargazersURL = "stargazers_url" 124 | case contributorsURL = "contributors_url" 125 | case subscribersURL = "subscribers_url" 126 | case subscriptionURL = "subscription_url" 127 | case commitsURL = "commits_url" 128 | case gitCommitsURL = "git_commits_url" 129 | case commentsURL = "comments_url" 130 | case issueCommentURL = "issue_comment_url" 131 | case contentsURL = "contents_url" 132 | case compareURL = "compare_url" 133 | case mergesURL = "merges_url" 134 | case archiveURL = "archive_url" 135 | case downloadsURL = "downloads_url" 136 | case issuesURL = "issues_url" 137 | case pullsURL = "pulls_url" 138 | case milestonesURL = "milestones_url" 139 | case notificationsURL = "notifications_url" 140 | case labelsURL = "labels_url" 141 | case releasesURL = "releases_url" 142 | case deploymentsURL = "deployments_url" 143 | case createdAt = "created_at" 144 | case updatedAt = "updated_at" 145 | case pushedAt = "pushed_at" 146 | case gitURL = "git_url" 147 | case sshURL = "ssh_url" 148 | case cloneURL = "clone_url" 149 | case svnURL = "svn_url" 150 | case homepage = "homepage" 151 | case size = "size" 152 | case stargazersCount = "stargazers_count" 153 | case watchersCount = "watchers_count" 154 | case language = "language" 155 | case hasIssues = "has_issues" 156 | case hasProjects = "has_projects" 157 | case hasDownloads = "has_downloads" 158 | case hasWiki = "has_wiki" 159 | case hasPages = "has_pages" 160 | case forksCount = "forks_count" 161 | case mirrorURL = "mirror_url" 162 | case archived = "archived" 163 | case disabled = "disabled" 164 | case openIssuesCount = "open_issues_count" 165 | case license = "license" 166 | case forks = "forks" 167 | case openIssues = "open_issues" 168 | case watchers = "watchers" 169 | case defaultBranch = "default_branch" 170 | case permissions = "permissions" 171 | case topics = "topics" 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Tag.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Tag: Codable { 5 | 6 | public struct List: Codable { 7 | 8 | public internal(set) var name: String 9 | public internal(set) var commit: Commit 10 | public internal(set) var zipballUrl: String? 11 | public internal(set) var tarballUrl: String? 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case name 15 | case commit 16 | case zipballUrl = "zipball_url" 17 | case tarballUrl = "tarball_url" 18 | } 19 | 20 | } 21 | 22 | public struct Object: Codable { 23 | 24 | public internal(set) var type: String? 25 | public internal(set) var sha: String? 26 | public internal(set) var url: String? 27 | 28 | enum CodingKeys: String, CodingKey { 29 | case type = "type" 30 | case sha = "sha" 31 | case url = "url" 32 | } 33 | 34 | } 35 | 36 | public internal(set) var nodeID: String? 37 | public internal(set) var tag: String? 38 | public internal(set) var sha: String? 39 | public internal(set) var url: String? 40 | public internal(set) var message: String? 41 | public internal(set) var tagger: Author? 42 | public internal(set) var object: Object? 43 | public internal(set) var verification: Verification? 44 | 45 | enum CodingKeys: String, CodingKey { 46 | case nodeID = "node_id" 47 | case tag = "tag" 48 | case sha = "sha" 49 | case url = "url" 50 | case message = "message" 51 | case tagger = "tagger" 52 | case object = "object" 53 | case verification = "verification" 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Verification.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Verification: Codable { 5 | 6 | public internal(set) var verified: Bool? 7 | public internal(set) var reason: String? 8 | public internal(set) var signature: String? 9 | public internal(set) var payload: String? 10 | 11 | enum CodingKeys: String, CodingKey { 12 | case verified = "verified" 13 | case reason = "reason" 14 | case signature = "signature" 15 | case payload = "payload" 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Model/Webhook.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct Webhook: Codable { 5 | 6 | public struct Post: Codable { 7 | 8 | public internal(set) var name: String 9 | public internal(set) var active: Bool 10 | public internal(set) var events: [EventType] 11 | public internal(set) var config: Config 12 | 13 | public init(active: Bool = true, events: [EventType] = [.push], config: Config) { 14 | self.name = "web" 15 | self.active = active 16 | self.events = events 17 | self.config = config 18 | } 19 | 20 | } 21 | 22 | public struct Config: Codable { 23 | 24 | public enum InsecureSSL: String, Codable { 25 | case yes = "1" 26 | case no = "0" 27 | } 28 | 29 | public internal(set) var contentType: String? 30 | public internal(set) var insecureSSL: InsecureSSL? 31 | public internal(set) var url: String 32 | public internal(set) var secret: String? 33 | 34 | enum CodingKeys: String, CodingKey { 35 | case contentType = "content_type" 36 | case insecureSSL = "insecure_ssl" 37 | case url = "url" 38 | case secret = "secret" 39 | } 40 | 41 | public init(contentType: String? = nil, insecureSSL: InsecureSSL? = .no, url: String, secret: String? = nil) { 42 | self.contentType = contentType 43 | self.insecureSSL = insecureSSL 44 | self.url = url 45 | self.secret = secret 46 | } 47 | 48 | } 49 | 50 | public struct LastResponse: Codable { 51 | 52 | public internal(set) var code: Int? 53 | public internal(set) var status: String? 54 | public internal(set) var message: String? 55 | 56 | } 57 | 58 | public enum EventType: String, Codable { 59 | 60 | /// Any time any event is triggered (Wildcard Event) 61 | case all = "*" 62 | 63 | // MARK: Repo hooks 64 | 65 | /// Triggered when a check run is created, rerequested, completed, or has a requested_action. 66 | case check_run 67 | 68 | /// Triggered when a check suite is completed, requested, or rerequested. 69 | case check_suite 70 | 71 | /// Triggered when a commit comment is created. 72 | case commit_comment 73 | 74 | /// Represents a created branch or tag. 75 | case create 76 | 77 | /// Represents a deleted branch or tag. 78 | case delete 79 | 80 | /// Triggered when an issue comment is created, edited, or deleted. 81 | case issue_comment 82 | 83 | /// Triggered when an issue is opened, edited, deleted, transferred, pinned, unpinned, closed, reopened, assigned, unassigned, labeled, unlabeled, locked, unlocked, milestoned, or demilestoned. 84 | case issues 85 | 86 | /// Triggered when a pull request is assigned, unassigned, labeled, unlabeled, opened, edited, closed, reopened, synchronize, ready_for_review, locked, unlocked or when a pull request review is requested or removed. 87 | case pull_request 88 | 89 | /// Triggered when a pull request review is submitted into a non-pending state, the body is edited, or the review is dismissed. 90 | case pull_request_review 91 | 92 | /// Triggered when a comment on a pull request's unified diff is created, edited, or deleted (in the Files Changed tab). 93 | case pull_request_review_comment 94 | 95 | /// Triggered on a push to a repository branch. Branch pushes and repository tag pushes also trigger webhook push events. This is the default event. 96 | case push 97 | 98 | /// Triggered when a release is published, unpublished, created, edited, deleted, or prereleased. 99 | case release 100 | 101 | // MARK: Organization hooks 102 | 103 | /// Triggered when an organization is deleted and renamed, and when a user is added, removed, or invited to an organization. Organization hooks only. 104 | case organization 105 | 106 | /// Triggered when a repository is created, archived, unarchived, renamed, edited, transferred, made public, or made private. Organization hooks are also triggered when a repository is deleted. 107 | case repository 108 | 109 | enum CodingKeys: String, CodingKey { 110 | case all 111 | case check_run 112 | case check_suite 113 | case commit_comment 114 | case create 115 | case delete 116 | case issue_comment 117 | case issues 118 | case pull_request 119 | case pull_request_review 120 | case pull_request_review_comment 121 | case push 122 | case release 123 | case organization 124 | case repository 125 | } 126 | 127 | } 128 | 129 | public internal(set) var type: String? 130 | public internal(set) var id: Int 131 | public internal(set) var name: String 132 | public internal(set) var active: Bool 133 | public internal(set) var events: [EventType] 134 | public internal(set) var config: Config 135 | public internal(set) var updatedAt: String? 136 | public internal(set) var createdAt: String? 137 | public internal(set) var url: String? 138 | public internal(set) var testURL: String? 139 | public internal(set) var pingURL: String? 140 | public internal(set) var lastResponse: LastResponse? 141 | 142 | enum CodingKeys: String, CodingKey { 143 | case type = "type" 144 | case id = "id" 145 | case name = "name" 146 | case active = "active" 147 | case events = "events" 148 | case config = "config" 149 | case updatedAt = "updated_at" 150 | case createdAt = "created_at" 151 | case url = "url" 152 | case testURL = "test_url" 153 | case pingURL = "ping_url" 154 | case lastResponse = "last_response" 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Query.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | public struct QueryableProperty { 5 | 6 | /// Queryable element accessor 7 | public var element: QueryableType? 8 | 9 | public let github: GitHubClient 10 | 11 | init(_ obj: QueryableType? = nil, github: GitHubClient) { 12 | element = obj 13 | self.github = github 14 | } 15 | 16 | } 17 | 18 | /// Queryable protocol 19 | public protocol Queryable { 20 | 21 | /// Supported testable type 22 | associatedtype ObjectType 23 | 24 | /// Main static function to access github queries 25 | static func query(on github: GitHubClient) -> QueryableProperty 26 | 27 | } 28 | 29 | 30 | extension Queryable { 31 | 32 | /// Main static function to access github queries 33 | public static func query(on github: GitHubClient) -> QueryableProperty { 34 | return QueryableProperty(github: github) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Branch+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Branch: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Branch { 8 | 9 | 10 | /// Get specific branch 11 | /// - Parameter org: Organization 12 | /// - Parameter repo: Repo 13 | /// - Parameter name: Branch name 14 | public func get(org: String, repo: String, branch name: String) throws -> EventLoopFuture { 15 | return try github.get(path: "repos/\(org)/\(repo)/branches/\(name)") 16 | } 17 | 18 | 19 | /// Get all available branches 20 | /// - Parameter org: Organization 21 | /// - Parameter repo: Repo 22 | public func get(org: String, repo: String) throws -> EventLoopFuture<[Branch]> { 23 | return try github.get(path: "repos/\(org)/\(repo)/branches") 24 | } 25 | 26 | } 27 | 28 | 29 | extension EventLoopFuture where Value == Branch { 30 | 31 | /// Latest commit on a branch 32 | public func latestCommit() -> EventLoopFuture { 33 | return map { branch in 34 | return branch.commit 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Comment+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Comment: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Comment { 8 | 9 | /// Get comments for an issue 10 | public func get(org: String, repo: String, issue: Int) throws -> EventLoopFuture<[Comment]> { 11 | return try github.get(path: "repos/\(org)/\(repo)/issues/\(issue)/comments") 12 | } 13 | 14 | /// Get comments for a PR 15 | public func get(org: String, repo: String, pr: Int) throws -> EventLoopFuture<[Comment]> { 16 | return try get(org: org, repo: repo, issue: pr) 17 | } 18 | 19 | /// All comments for a repo 20 | public func get(org: String, repo: String) throws -> EventLoopFuture<[Comment]> { 21 | return try github.get(path: "repos/\(org)/\(repo)/issues/comments") 22 | } 23 | 24 | /// Get a single comment 25 | public func get(org: String, repo: String, comment: Int) throws -> EventLoopFuture { 26 | return try github.get(path: "repos/\(org)/\(repo)/issues/comments/\(comment)") 27 | } 28 | 29 | /// Post a new comment to an issue 30 | public func create(org: String, repo: String, issue: Int, message: String) throws -> EventLoopFuture { 31 | let message = Comment.Post(body: message) 32 | return try github.post(path: "repos/\(org)/\(repo)/issues/\(issue)/comments", post: message) 33 | } 34 | 35 | /// Post a new comment to a PR 36 | public func create(org: String, repo: String, pr: Int, message: String) throws -> EventLoopFuture { 37 | return try create(org: org, repo: repo, issue: pr, message: message) 38 | } 39 | 40 | /// Update an existing comment 41 | public func update(org: String, repo: String, comment: Int, message: String) throws -> EventLoopFuture { 42 | let message = Comment.Post(body: message) 43 | return try github.patch(path: "repos/\(org)/\(repo)/issues/comments/\(comment)", post: message) 44 | } 45 | 46 | /// Delete an existing comment 47 | public func delete(org: String, repo: String, comment: Int) throws -> EventLoopFuture { 48 | return try github.delete(path: "repos/\(org)/\(repo)/issues/comments/\(comment)") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Commit+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Commit: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Commit { 8 | 9 | /// Get commits 10 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture<[Commit]> { 11 | return try github.get(path: "repos/\(org)/\(repo)/commits") 12 | } 13 | 14 | /// Get commit detail 15 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture { 16 | return try github.get(path: "repos/\(org)/\(repo)/commits/\(sha)") 17 | } 18 | 19 | /// Get branches where commit is HEAD 20 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture<[Branch]> { 21 | return try github.get(path: "repos/\(org)/\(repo)/commits/\(sha)/branches-where-head") 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/File+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension File: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == File { 8 | 9 | /// Get file detail 10 | public func get(org: String, repo: String, ref: String? = nil, path: String) throws -> EventLoopFuture { 11 | var path = "repos/\(org)/\(repo)/contents/\(path)" 12 | if let ref = ref { 13 | path.append("?ref=\(ref)") 14 | } 15 | return try github.get(path: path) 16 | } 17 | 18 | } 19 | 20 | 21 | extension EventLoopFuture where Value == File { 22 | 23 | /// Download file content if available 24 | public func download(on github: GitHub) throws -> EventLoopFuture { 25 | return flatMap { file in 26 | guard file.type == "file" else { 27 | return github.eventLoop.makeFailedFuture(File.Error.notFile(file.type)) 28 | } 29 | if let base64Content = file.content, let data = Data(base64Encoded: base64Content, options: [.ignoreUnknownCharacters]) { 30 | return github.eventLoop.makeSucceededFuture(data) 31 | } 32 | guard let downloadUrl = file.downloadURL else { 33 | return github.eventLoop.makeFailedFuture(File.Error.missingDownloadUrl) 34 | } 35 | do { 36 | return try github.get(file: downloadUrl).flatMapThrowing({ data in 37 | guard let data = data else { 38 | throw File.Error.missingDownloadUrl 39 | } 40 | return data 41 | }) 42 | } catch { 43 | return github.eventLoop.makeFailedFuture(error) 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Git/GitBlob+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension GitBlob: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == GitBlob { 8 | 9 | 10 | /// Get specific git file (blob) 11 | /// - Parameter org: Organization 12 | /// - Parameter repo: Repo 13 | /// - Parameter sha: SHA of the file 14 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture { 15 | return try github.get(path: "repos/\(org)/\(repo)/git/blobs/\(sha)") 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Git/GitCommit+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension GitCommit: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == GitCommit { 8 | 9 | 10 | /// Get specific git commit 11 | /// - Parameter org: Organization 12 | /// - Parameter repo: Repo 13 | /// - Parameter sha: SHA of the commit 14 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture { 15 | return try github.get(path: "repos/\(org)/\(repo)/git/commits/\(sha)") 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Git/GitTree+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension GitTree: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == GitTree { 8 | 9 | 10 | /// Get specific git tree 11 | /// - Parameter org: Organization 12 | /// - Parameter repo: Repo 13 | /// - Parameter sha: SHA of the tree 14 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture { 15 | return try github.get(path: "repos/\(org)/\(repo)/git/trees/\(sha)") 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Organization+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Organization: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Organization { 8 | 9 | /// Get organization detail 10 | public func get(name: String) throws -> EventLoopFuture { 11 | return try github.get(path: "orgs/\(name)") 12 | } 13 | 14 | /// Get all organizations for a user 15 | public func get(user: String) throws -> EventLoopFuture<[Organization]> { 16 | return try github.get(path: "users/\(user)/orgs") 17 | } 18 | 19 | /// Get all organizations for authenticated user 20 | public func get() throws -> EventLoopFuture<[Organization]> { 21 | return try github.get(path: "user/orgs") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/PR+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension PR: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == PR { 8 | 9 | /// Get pull requests 10 | public func get(org: String, repo: String) throws -> EventLoopFuture<[PR]> { 11 | return try github.get(path: "repos/\(org)/\(repo)/pulls") 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Release+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Release: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Release { 8 | 9 | /// Get a release 10 | /// - Parameter org: Organization 11 | /// - Parameter repo: Repo 12 | /// - Parameter id: Release ID 13 | public func get(org: String, repo: String, id: String) throws -> EventLoopFuture { 14 | return try github.get(path: "repos/\(org)/\(repo)/releases/\(id)") 15 | } 16 | 17 | /// Get the latest release 18 | /// - Parameter org: Organization 19 | /// - Parameter repo: Repo 20 | public func latest(org: String, repo: String) throws -> EventLoopFuture { 21 | return try github.get(path: "repos/\(org)/\(repo)/releases/latest") 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Repo+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Repo: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Repo { 8 | 9 | /// Get organization detail 10 | public func get(org: String, repo: String) throws -> EventLoopFuture { 11 | return try github.get(path: "repos/\(org)/\(repo)") 12 | } 13 | 14 | /// Get all repos for a user 15 | public func get(user: String) throws -> EventLoopFuture<[Repo]> { 16 | return try github.get(path: "users/\(user)/repos") 17 | } 18 | 19 | /// Get all repos for an organization 20 | public func get(org: String) throws -> EventLoopFuture<[Repo]> { 21 | return try github.get(path: "orgs/\(org)/repos") 22 | } 23 | 24 | /// Get all organizations for authenticated user 25 | public func get() throws -> EventLoopFuture<[Repo]> { 26 | return try github.get(path: "user/repos") 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Tag+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Tag: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Tag { 8 | 9 | /// Get a tag 10 | /// - Parameter org: Organization 11 | /// - Parameter repo: Repo 12 | /// - Parameter sha: SHA of the tag 13 | public func get(org: String, repo: String, sha: String) throws -> EventLoopFuture { 14 | return try github.get(path: "repos/\(org)/\(repo)/git/tags/\(sha)") 15 | } 16 | 17 | /// Get all tags for a repo 18 | /// - Parameter org: Organization 19 | /// - Parameter repo: Repo 20 | public func get(org: String, repo: String) throws -> EventLoopFuture<[Tag.List]> { 21 | return try github.get(path: "repos/\(org)/\(repo)/tags") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sources/GitHubKit/Requests/Webhook+Requests.swift: -------------------------------------------------------------------------------- 1 | import NIO 2 | 3 | 4 | extension Webhook: Queryable { } 5 | 6 | 7 | extension QueryableProperty where QueryableType == Webhook { 8 | 9 | /// Get specific webhook 10 | public func get(org: String, repo: String, hook id: Int) throws -> EventLoopFuture { 11 | return try github.get(path: "repos/\(org)/\(repo)/hooks/\(id)") 12 | } 13 | 14 | /// Test a specific webhook 15 | public func test(org: String, repo: String, hook id: Int) throws -> EventLoopFuture { 16 | let future: EventLoopFuture = try github.post(path: "repos/\(org)/\(repo)/hooks/\(id)/tests", post: [] as [String]) 17 | return future.map { _ in 18 | return Void() 19 | } 20 | } 21 | 22 | /// Create a webhook 23 | public func create(org: String, repo: String, hook: Webhook.Post) throws -> EventLoopFuture { 24 | let future: EventLoopFuture = try github.post(path: "repos/\(org)/\(repo)/hooks", post: hook) 25 | return future 26 | } 27 | 28 | /// Edit a specific webhook 29 | public func edit(org: String, repo: String, id: Int, hook: Webhook.Post) throws -> EventLoopFuture { 30 | let future: EventLoopFuture = try github.patch(path: "repos/\(org)/\(repo)/hooks/\(id)", post: hook) 31 | return future 32 | } 33 | 34 | /// Get all available webhooks 35 | public func get(org: String, repo: String) throws -> EventLoopFuture<[Webhook]> { 36 | return try github.get(path: "repos/\(org)/\(repo)/hooks") 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Sources/GitHubKitMocks/Exports.swift: -------------------------------------------------------------------------------- 1 | @_exported import GitHubKit 2 | -------------------------------------------------------------------------------- /Sources/GitHubKitMocks/GitHubMock.swift: -------------------------------------------------------------------------------- 1 | import GitHubKit 2 | import NIOHTTP1 3 | import NIO 4 | 5 | 6 | public protocol MockResponseConvertible { 7 | var mockResponse: GitHubMock.MockResponse { get } 8 | } 9 | 10 | 11 | public class GitHubMock: GitHubClient { 12 | 13 | public struct MockResponse: MockResponseConvertible, Hashable { 14 | 15 | public let method: HTTPMethod 16 | public let path: String 17 | 18 | public init(_ method: HTTPMethod = .GET, _ path: String) { 19 | self.method = method 20 | self.path = path 21 | } 22 | 23 | public var mockResponse: MockResponse { 24 | return self 25 | } 26 | 27 | public func hash(into hasher: inout Hasher) { 28 | hasher.combine(path) 29 | hasher.combine(method.rawValue) 30 | } 31 | 32 | } 33 | 34 | public var eventLoop = EmbeddedEventLoop() 35 | 36 | public var responses: [MockResponse: Decodable?] = [:] 37 | 38 | public func add(response object: C? = nil, path: String, method: HTTPMethod = .GET) where C: Decodable { 39 | responses[MockResponse(method, path)] = object 40 | } 41 | 42 | func response(_ method: HTTPMethod, _ path: String) -> C? where C: Decodable { 43 | let key = MockResponse(method, path) 44 | let ret = responses[key] 45 | print(C.self) 46 | return ret as? C 47 | } 48 | 49 | func future(response method: HTTPMethod, _ path: String) -> EventLoopFuture where C: Decodable { 50 | let r: C? = response(.GET, path) 51 | guard let d: C = r else { 52 | return eventLoop.makeFailedFuture(GitHub.Error.notFound(path)) 53 | } 54 | return eventLoop.makeSucceededFuture(d) 55 | } 56 | 57 | public func get(path: String) throws -> EventLoopFuture where C : Decodable { 58 | return future(response: .GET, path) 59 | } 60 | 61 | public func post(path: String, post: E) throws -> EventLoopFuture where C : Decodable, E : Encodable { 62 | return future(response: .POST, path) 63 | } 64 | 65 | public func put(path: String, post: E) throws -> EventLoopFuture where C : Decodable, E : Encodable { 66 | return future(response: .PUT, path) 67 | } 68 | 69 | public func patch(path: String, post: E) throws -> EventLoopFuture where C : Decodable, E : Encodable { 70 | return future(response: .PATCH, path) 71 | } 72 | 73 | public func delete(path: String) throws -> EventLoopFuture { 74 | guard let _ = responses.index(forKey: GitHubMock.MockResponse(.DELETE, path)) else { 75 | return eventLoop.makeFailedFuture(GitHub.Error.notFound(path)) 76 | } 77 | return eventLoop.makeSucceededFuture(Void()) 78 | } 79 | 80 | public var files: [String: Data] = [:] 81 | 82 | public func get(file path: String) throws -> EventLoopFuture { 83 | guard let file = files[path] else { 84 | return eventLoop.makeSucceededFuture(nil) 85 | } 86 | return eventLoop.makeSucceededFuture(file) 87 | } 88 | 89 | public var redirects: [String: String] = [:] 90 | 91 | public func redirect(file path: String, status: HTTPResponseStatus = .found) throws -> EventLoopFuture { 92 | guard let redirect = redirects[path] else { 93 | return eventLoop.makeFailedFuture(GitHub.Error.notFound(path)) 94 | } 95 | return eventLoop.makeSucceededFuture(redirect) 96 | } 97 | 98 | /// Download links 99 | /// - Note: Repo is a key to return a download link 100 | public var downloads: [String: String] = [:] 101 | 102 | public func download(org: String, repo: String, ref: String, format: GitHub.Format = .tarball) throws -> EventLoopFuture { 103 | guard let download = downloads[repo] else { 104 | return eventLoop.makeFailedFuture(GitHub.Error.notFound("Download for \(org)/\(repo)/\(ref)")) 105 | } 106 | return eventLoop.makeSucceededFuture(download) 107 | } 108 | 109 | public func syncShutdown() throws { } 110 | 111 | public init() { } 112 | 113 | } 114 | 115 | 116 | extension String: MockResponseConvertible { 117 | 118 | public var mockResponse: GitHubMock.MockResponse { 119 | return GitHubMock.MockResponse(.GET, self) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Sources/GitHubKitMocksTests/Tests.swift: -------------------------------------------------------------------------------- 1 | import GitHubKitMocks 2 | @testable import GitHubKit 3 | import XCTest 4 | 5 | 6 | class Tests: XCTestCase { 7 | 8 | var github: GitHubMock! 9 | 10 | override func setUp() { 11 | super.setUp() 12 | 13 | github = GitHubMock() 14 | } 15 | 16 | func testGet() { 17 | let branch = Branch( 18 | name: "test", 19 | commit: Commit(sha: "test-sha", url: "test://url") 20 | ) 21 | let res = [branch] 22 | github.add(response: res, path: "repos/org/repo/branches", method: .GET) 23 | let r = try! Branch.query(on: github).get(org: "org", repo: "repo").wait() 24 | 25 | XCTAssertEqual(res[0].name, r[0].name) 26 | } 27 | 28 | } 29 | --------------------------------------------------------------------------------