diff --git a/Carthage/Checkouts/Argo b/Carthage/Checkouts/Argo index be127e5..f494b05 160000 --- a/Carthage/Checkouts/Argo +++ b/Carthage/Checkouts/Argo @@ -1 +1 @@ -Subproject commit be127e583674ff8e92653c9bbd0328ab55daebc7 +Subproject commit f494b057b12c36239892a820262b566f09a5275d diff --git a/Carthage/Checkouts/Curry b/Carthage/Checkouts/Curry index c38c007..6d1bd5a 160000 --- a/Carthage/Checkouts/Curry +++ b/Carthage/Checkouts/Curry @@ -1 +1 @@ -Subproject commit c38c0072a4894637afbc773716e5bf05dd3462c7 +Subproject commit 6d1bd5a772ede007ff5422a2abe1f26a42fde563 diff --git a/Carthage/Checkouts/ReactiveCocoa b/Carthage/Checkouts/ReactiveCocoa index 6f52f8b..fdbd367 160000 --- a/Carthage/Checkouts/ReactiveCocoa +++ b/Carthage/Checkouts/ReactiveCocoa @@ -1 +1 @@ -Subproject commit 6f52f8bbdf1118431b3984a1e2858e6eecf444e6 +Subproject commit fdbd3676cc4305c48e53bc25aebdb0eb607cdc2b diff --git a/Carthage/Checkouts/Result b/Carthage/Checkouts/Result index 47058fb..5dec9cf 160000 --- a/Carthage/Checkouts/Result +++ b/Carthage/Checkouts/Result @@ -1 +1 @@ -Subproject commit 47058fbca5e3b9dcb6597be23079148584e635ef +Subproject commit 5dec9cf225d5fa3b0602679fb2638e40191da46b diff --git a/Tentacle.xcodeproj/project.pbxproj b/Tentacle.xcodeproj/project.pbxproj index 6ec66c2..c4c5aed 100644 --- a/Tentacle.xcodeproj/project.pbxproj +++ b/Tentacle.xcodeproj/project.pbxproj @@ -23,6 +23,26 @@ 619081891C8A2DB7001BE2F8 /* GitHubErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEB0765E1C8A019A00ABD373 /* GitHubErrorTests.swift */; }; 6190818A1C8A2DB7001BE2F8 /* ReleaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE0F40A11C8909EB00E3B11A /* ReleaseTests.swift */; }; 6190818B1C8A2DB7001BE2F8 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 7A1A82561CF3DBAC0076E2DD /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82551CF3DBAC0076E2DD /* Issue.swift */; }; + 7A1A82581CF3DE4C0076E2DD /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82571CF3DE4C0076E2DD /* Label.swift */; }; + 7A1A825A1CF3DEEA0076E2DD /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82591CF3DEEA0076E2DD /* Milestone.swift */; }; + 7A1A825E1CF3E5B60076E2DD /* PullRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A825D1CF3E5B60076E2DD /* PullRequest.swift */; }; + 7A1F20ED1D3E85BB00F275F8 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EC1D3E85BB00F275F8 /* Color.swift */; }; + 7A1F20EE1D3E862200F275F8 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EC1D3E85BB00F275F8 /* Color.swift */; }; + 7A1F20F01D3E86D900F275F8 /* ColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EF1D3E86D900F275F8 /* ColorTests.swift */; }; + 7A1F20F11D3E872800F275F8 /* ColorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EF1D3E86D900F275F8 /* ColorTests.swift */; }; + 7A1F20F21D3E873500F275F8 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EC1D3E85BB00F275F8 /* Color.swift */; }; + 7A1F20F31D3E873600F275F8 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F20EC1D3E85BB00F275F8 /* Color.swift */; }; + 7A2F66101CF527EB00463602 /* IssuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAB00FC1CF51FC5005A7319 /* IssuesTests.swift */; }; + 7A2F66111CF527FB00463602 /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82551CF3DBAC0076E2DD /* Issue.swift */; }; + 7A2F66121CF5280200463602 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82571CF3DE4C0076E2DD /* Label.swift */; }; + 7A2F66131CF5280600463602 /* Milestone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A82591CF3DEEA0076E2DD /* Milestone.swift */; }; + 7A2F66141CF5281200463602 /* PullRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A825D1CF3E5B60076E2DD /* PullRequest.swift */; }; + 7A2F66171CF53F6300463602 /* repos-Palleas-opensource-Sample-repository-issues.data in Resources */ = {isa = PBXBuildFile; fileRef = 7A2F660C1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.data */; }; + 7A2F66181CF53F6300463602 /* repos-Palleas-opensource-Sample-repository-issues.response in Resources */ = {isa = PBXBuildFile; fileRef = 7A2F660D1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.response */; }; + 7A2F66191CF53F6400463602 /* repos-Palleas-opensource-Sample-repository-issues.data in Resources */ = {isa = PBXBuildFile; fileRef = 7A2F660C1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.data */; }; + 7A2F661A1CF53F6400463602 /* repos-Palleas-opensource-Sample-repository-issues.response in Resources */ = {isa = PBXBuildFile; fileRef = 7A2F660D1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.response */; }; + 7AAB00FD1CF51FC5005A7319 /* IssuesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAB00FC1CF51FC5005A7319 /* IssuesTests.swift */; }; BE0F40A01C89098E00E3B11A /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE0F409F1C89098E00E3B11A /* Fixture.swift */; }; BE0F40A21C8909EB00E3B11A /* ReleaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE0F40A11C8909EB00E3B11A /* ReleaseTests.swift */; }; BE0F40A41C89135D00E3B11A /* Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE0F40A31C89135D00E3B11A /* Decodable.swift */; }; @@ -121,6 +141,15 @@ /* Begin PBXFileReference section */ 61377C6C1C8A2B2C0081FF24 /* Tentacle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Tentacle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6190817D1C8A2D2B001BE2F8 /* Tentacle-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tentacle-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A1A82551CF3DBAC0076E2DD /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Issue.swift; sourceTree = ""; }; + 7A1A82571CF3DE4C0076E2DD /* Label.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; + 7A1A82591CF3DEEA0076E2DD /* Milestone.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Milestone.swift; sourceTree = ""; }; + 7A1A825D1CF3E5B60076E2DD /* PullRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PullRequest.swift; sourceTree = ""; }; + 7A1F20EC1D3E85BB00F275F8 /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 7A1F20EF1D3E86D900F275F8 /* ColorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorTests.swift; sourceTree = ""; }; + 7A2F660C1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.data */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "repos-Palleas-opensource-Sample-repository-issues.data"; sourceTree = ""; }; + 7A2F660D1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.response */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "repos-Palleas-opensource-Sample-repository-issues.response"; sourceTree = ""; }; + 7AAB00FC1CF51FC5005A7319 /* IssuesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssuesTests.swift; sourceTree = ""; }; BE0F409F1C89098E00E3B11A /* Fixture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Fixture.swift; path = Tests/Fixture.swift; sourceTree = SOURCE_ROOT; }; BE0F40A11C8909EB00E3B11A /* ReleaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReleaseTests.swift; sourceTree = ""; }; BE0F40A31C89135D00E3B11A /* Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decodable.swift; sourceTree = ""; }; @@ -233,6 +262,8 @@ BE0F40961C8908CB00E3B11A /* Fixtures */ = { isa = PBXGroup; children = ( + 7A2F660C1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.data */, + 7A2F660D1CF523F300463602 /* repos-Palleas-opensource-Sample-repository-issues.response */, BE0F40D81C8CA13400E3B11A /* repos-Carthage-Carthage-releases-tags-0.15.data */, BE0F40D91C8CA13400E3B11A /* repos-Carthage-Carthage-releases-tags-0.15.response */, BE1E035A1C964F8B001296C2 /* repos-Carthage-Carthage-releases.page-1-per_page-30.data */, @@ -282,19 +313,24 @@ BE88E7F41C88C6B30034A112 /* Tentacle */ = { isa = PBXGroup; children = ( - BE88E7F51C88C6B30034A112 /* Tentacle.h */, BEEE47441C91BB3A000FFC21 /* ArgoExtensions.swift */, BE88E80C1C88C72B0034A112 /* Client.swift */, + 7A1F20EC1D3E85BB00F275F8 /* Color.swift */, BE0F40A31C89135D00E3B11A /* Decodable.swift */, BECB8A8D1CBDD919005D70A6 /* FoundationExtensions.swift */, BEB0765C1C8A001C00ABD373 /* GitHubError.swift */, + BE88E7F71C88C6B30034A112 /* Info.plist */, + 7A1A82551CF3DBAC0076E2DD /* Issue.swift */, + 7A1A82571CF3DE4C0076E2DD /* Label.swift */, + 7A1A82591CF3DEEA0076E2DD /* Milestone.swift */, + 7A1A825D1CF3E5B60076E2DD /* PullRequest.swift */, BE88E8311C88D33D0034A112 /* Release.swift */, BE88E82F1C88CDAB0034A112 /* Repository.swift */, BEEE47411C91B8DF000FFC21 /* ResourceType.swift */, BEEE474D1C92623E000FFC21 /* Response.swift */, BE88E82C1C88C94D0034A112 /* Server.swift */, + BE88E7F51C88C6B30034A112 /* Tentacle.h */, BECB8A8A1CBDCD17005D70A6 /* User.swift */, - BE88E7F71C88C6B30034A112 /* Info.plist */, ); path = Tentacle; sourceTree = ""; @@ -303,15 +339,17 @@ isa = PBXGroup; children = ( BE0F40961C8908CB00E3B11A /* Fixtures */, - BE0F409F1C89098E00E3B11A /* Fixture.swift */, BE2B6A6B1C8B854F0080BFEB /* ClientTests.swift */, + BE0F409F1C89098E00E3B11A /* Fixture.swift */, BEB0765E1C8A019A00ABD373 /* GitHubErrorTests.swift */, + BE88E8031C88C6B30034A112 /* Info.plist */, + 7AAB00FC1CF51FC5005A7319 /* IssuesTests.swift */, BE0F40A11C8909EB00E3B11A /* ReleaseTests.swift */, BEA86F9C1C9F7E1E0049360B /* RepositoryTests.swift */, BE1E036A1C9AD87F001296C2 /* ResponseTests.swift */, BEA86F991C9F7C230049360B /* ServerTests.swift */, BECB8AA11CBDDF0F005D70A6 /* UserTests.swift */, - BE88E8031C88C6B30034A112 /* Info.plist */, + 7A1F20EF1D3E86D900F275F8 /* ColorTests.swift */, ); path = Tests; sourceTree = ""; @@ -519,8 +557,10 @@ BECB8A9F1CBDDE4C005D70A6 /* users-test.data in Resources */, BE0F40E61C8CA13900E3B11A /* repos-mdiep-NonExistent-releases-tags-tag.data in Resources */, BECB8A9D1CBDDE4C005D70A6 /* users-mdiep.data in Resources */, + 7A2F661A1CF53F6400463602 /* repos-Palleas-opensource-Sample-repository-issues.response in Resources */, BE0F40E71C8CA13900E3B11A /* repos-mdiep-NonExistent-releases-tags-tag.response in Resources */, BE0F40EF1C8CA1E100E3B11A /* repos-torvalds-linux-releases-tags-v4.4.response in Resources */, + 7A2F66191CF53F6400463602 /* repos-Palleas-opensource-Sample-repository-issues.data in Resources */, BE1E03671C964F98001296C2 /* repos-Carthage-Carthage-releases.page-1-per_page-30.response in Resources */, BE1E03701C9CF849001296C2 /* repos-mdiep-MDPSplitView-releases-tags-1.0.2.data in Resources */, BEAB175C1C9D0AB7009F8F58 /* repos-mdiep-MDPSplitView-releases-assets-433845.response in Resources */, @@ -551,8 +591,10 @@ BECB8A9B1CBDDE4B005D70A6 /* users-test.data in Resources */, BE0F40E21C8CA13800E3B11A /* repos-mdiep-NonExistent-releases-tags-tag.data in Resources */, BECB8A991CBDDE4B005D70A6 /* users-mdiep.data in Resources */, + 7A2F66181CF53F6300463602 /* repos-Palleas-opensource-Sample-repository-issues.response in Resources */, BE0F40E31C8CA13800E3B11A /* repos-mdiep-NonExistent-releases-tags-tag.response in Resources */, BE0F40ED1C8CA1E000E3B11A /* repos-torvalds-linux-releases-tags-v4.4.response in Resources */, + 7A2F66171CF53F6300463602 /* repos-Palleas-opensource-Sample-repository-issues.data in Resources */, BE1E03631C964F98001296C2 /* repos-Carthage-Carthage-releases.page-1-per_page-30.response in Resources */, BE1E036F1C9CF849001296C2 /* repos-mdiep-MDPSplitView-releases-tags-1.0.2.data in Resources */, BEAB175E1C9D0AB8009F8F58 /* repos-mdiep-MDPSplitView-releases-assets-433845.response in Resources */, @@ -585,14 +627,19 @@ BEEE47461C91BB3A000FFC21 /* ArgoExtensions.swift in Sources */, 61377C791C8A2B760081FF24 /* Client.swift in Sources */, BEEE47431C91B8DF000FFC21 /* ResourceType.swift in Sources */, + 7A2F66141CF5281200463602 /* PullRequest.swift in Sources */, 61377C7C1C8A2B760081FF24 /* Release.swift in Sources */, + 7A1F20EE1D3E862200F275F8 /* Color.swift in Sources */, BEEE474F1C92623E000FFC21 /* Response.swift in Sources */, + 7A2F66121CF5280200463602 /* Label.swift in Sources */, + 7A2F66131CF5280600463602 /* Milestone.swift in Sources */, 61377C7D1C8A2B760081FF24 /* Repository.swift in Sources */, BECB8A8F1CBDD91D005D70A6 /* FoundationExtensions.swift in Sources */, 61377C7B1C8A2B760081FF24 /* GitHubError.swift in Sources */, 61377C7E1C8A2B760081FF24 /* Server.swift in Sources */, 61377C7A1C8A2B760081FF24 /* Decodable.swift in Sources */, BECB8A901CBDD920005D70A6 /* User.swift in Sources */, + 7A2F66111CF527FB00463602 /* Issue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -600,9 +647,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7A1F20F11D3E872800F275F8 /* ColorTests.swift in Sources */, 619081881C8A2DB7001BE2F8 /* Fixture.swift in Sources */, BE2B6A6D1C8B854F0080BFEB /* ClientTests.swift in Sources */, BEA86F9B1C9F7C230049360B /* ServerTests.swift in Sources */, + 7A2F66101CF527EB00463602 /* IssuesTests.swift in Sources */, + 7A1F20F31D3E873600F275F8 /* Color.swift in Sources */, BEA86F9E1C9F7E1E0049360B /* RepositoryTests.swift in Sources */, 6190818B1C8A2DB7001BE2F8 /* (null) in Sources */, 619081891C8A2DB7001BE2F8 /* GitHubErrorTests.swift in Sources */, @@ -619,14 +669,19 @@ BEEE47451C91BB3A000FFC21 /* ArgoExtensions.swift in Sources */, BE88E82D1C88C94D0034A112 /* Server.swift in Sources */, BEEE47421C91B8DF000FFC21 /* ResourceType.swift in Sources */, + 7A1A825E1CF3E5B60076E2DD /* PullRequest.swift in Sources */, BEB0765D1C8A001C00ABD373 /* GitHubError.swift in Sources */, + 7A1F20ED1D3E85BB00F275F8 /* Color.swift in Sources */, BEEE474E1C92623E000FFC21 /* Response.swift in Sources */, + 7A1A82581CF3DE4C0076E2DD /* Label.swift in Sources */, + 7A1A825A1CF3DEEA0076E2DD /* Milestone.swift in Sources */, BE88E8321C88D33D0034A112 /* Release.swift in Sources */, BE88E80D1C88C72B0034A112 /* Client.swift in Sources */, BE0F40A41C89135D00E3B11A /* Decodable.swift in Sources */, BECB8A8B1CBDCD17005D70A6 /* User.swift in Sources */, BECB8A8E1CBDD919005D70A6 /* FoundationExtensions.swift in Sources */, BE88E8301C88CDAB0034A112 /* Repository.swift in Sources */, + 7A1A82561CF3DBAC0076E2DD /* Issue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -634,12 +689,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7AAB00FD1CF51FC5005A7319 /* IssuesTests.swift in Sources */, BE1E036B1C9AD87F001296C2 /* ResponseTests.swift in Sources */, + 7A1F20F21D3E873500F275F8 /* Color.swift in Sources */, BEA86F9D1C9F7E1E0049360B /* RepositoryTests.swift in Sources */, BEB076601C8A019E00ABD373 /* GitHubErrorTests.swift in Sources */, BE0F40A01C89098E00E3B11A /* Fixture.swift in Sources */, BECB8AA31CBDDF0F005D70A6 /* UserTests.swift in Sources */, BE2B6A6C1C8B854F0080BFEB /* ClientTests.swift in Sources */, + 7A1F20F01D3E86D900F275F8 /* ColorTests.swift in Sources */, BEA86F9A1C9F7C230049360B /* ServerTests.swift in Sources */, BE0F40A21C8909EB00E3B11A /* ReleaseTests.swift in Sources */, ); diff --git a/Tentacle/ArgoExtensions.swift b/Tentacle/ArgoExtensions.swift index 450c477..a6ed919 100644 --- a/Tentacle/ArgoExtensions.swift +++ b/Tentacle/ArgoExtensions.swift @@ -10,7 +10,6 @@ import Argo import Foundation import Result - internal func decode(object: AnyObject) -> Result { let decoded: Decoded = decode(object) switch decoded { @@ -35,6 +34,22 @@ internal func toString(number: Int) -> Decoded { return .Success(number.description) } +internal func toInt(string: String) -> Decoded { + if let int = Int(string) { + return .Success(int) + } else { + return .Failure(.Custom("String is not a valid number")) + } +} + +internal func toIssueState(string: String) -> Decoded { + if let state = Issue.State(rawValue: string) { + return .Success(state) + } else { + return .Failure(.Custom("String \(string) does not represent a valid issue state")) + } +} + internal func toNSDate(string: String) -> Decoded { if let date = NSDateFormatter.ISO8601.dateFromString(string) { return .Success(date) @@ -42,3 +57,24 @@ internal func toNSDate(string: String) -> Decoded { return .Failure(.Custom("Date is not ISO8601 formatted")) } } + +internal func toOptionalNSDate(string: String?) -> Decoded { + guard let string = string else { return .Success(nil) } + if let date = NSDateFormatter.ISO8601.dateFromString(string) { + return .Success(date) + } else { + return .Failure(.Custom("Date is not ISO8601 formatted")) + } +} + +internal func toNSURL(string: String) -> Decoded { + if let url = NSURL(string: string) { + return .Success(url) + } else { + return .Failure(.Custom("URL is not properly formatted")) + } +} + +internal func toColor(string: String) -> Decoded { + return .Success(Color(hex: string)) +} diff --git a/Tentacle/Client.swift b/Tentacle/Client.swift index bca8961..f816e2d 100644 --- a/Tentacle/Client.swift +++ b/Tentacle/Client.swift @@ -161,6 +161,12 @@ public final class Client { // https://developer.github.com/v3/users/#get-a-single-user case UserInfo(login: String) + + // https://developer.github.com/v3/issues/#list-issues + case AssignedIssues + + // https://developer.github.com/v3/issues/#list-issues-for-a-repository + case IssuesInRepository(owner: String, repository: String) var path: String { switch self { @@ -170,6 +176,10 @@ public final class Client { return "/repos/\(owner)/\(repo)/releases" case let .UserInfo(login): return "/users/\(login)" + case .AssignedIssues: + return "/issues" + case .IssuesInRepository(let owner, let repository): + return "/repos/\(owner)/\(repository)/issues" } } @@ -181,6 +191,10 @@ public final class Client { return owner.hashValue ^ repo.hashValue case let .UserInfo(login): return login.hashValue + case .AssignedIssues: + return "AssignedIssues".hashValue + case .IssuesInRepository(let owner, let repository): + return "Issues".hashValue ^ owner.hashValue ^ repository.hashValue } } @@ -256,7 +270,16 @@ public final class Client { public func userWithLogin(login: String) -> SignalProducer<(Response, UserInfo), Error> { return fetchOne(.UserInfo(login: login)) } - + + public func assignedIssues(page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { + return fetchMany(.AssignedIssues, page: page, pageSize: perPage) + } + + public func issuesInRepository(repository: Repository, page: UInt = 1, perPage: UInt = 30) -> SignalProducer<(Response, [Issue]), Error> { + precondition(repository.server == server) + return fetchMany(.IssuesInRepository(owner: repository.owner, repository: repository.name), page: page, pageSize: perPage) + } + /// Fetch an endpoint from the API. private func fetch(endpoint: Endpoint, page: UInt?, pageSize: UInt?) -> SignalProducer<(Response, AnyObject), Error> { let URL = NSURL(server, endpoint, page: page, pageSize: pageSize) diff --git a/Tentacle/Color.swift b/Tentacle/Color.swift new file mode 100644 index 0000000..4828bdf --- /dev/null +++ b/Tentacle/Color.swift @@ -0,0 +1,32 @@ +// +// Color.swift +// Tentacle +// +// Created by Romain Pouclet on 2016-07-19. +// Copyright © 2016 Matt Diephouse. All rights reserved. +// + +import Foundation + +#if os(iOS) || os(tvOS) + import UIKit + public typealias Color = UIColor +#else + import Cocoa + public typealias Color = NSColor +#endif + +extension Color { + convenience init(hex: String) { + precondition(hex.characters.count == 6) + + let scanner = NSScanner(string: hex) + var rgb: UInt32 = 0 + scanner.scanHexInt(&rgb) + + let r = CGFloat((rgb & 0xff0000) >> 16) / 255.0 + let g = CGFloat((rgb & 0x00ff00) >> 8) / 255.0 + let b = CGFloat((rgb & 0x0000ff) >> 0) / 255.0 + self.init(red: r, green: g, blue: b, alpha: 1) + } +} \ No newline at end of file diff --git a/Tentacle/Issue.swift b/Tentacle/Issue.swift new file mode 100644 index 0000000..2f21190 --- /dev/null +++ b/Tentacle/Issue.swift @@ -0,0 +1,136 @@ +// +// Issue.swift +// Tentacle +// +// Created by Romain Pouclet on 2016-05-23. +// Copyright © 2016 Matt Diephouse. All rights reserved. +// + +import Argo +import Curry + +/// An Issue on Github +public struct Issue: Hashable, CustomStringConvertible { + public enum State: String { + case Open = "open" + case Closed = "closed" + } + + /// The id of the issue + public let ID: String + + /// The URL to view this issue in a browser + public let URL: NSURL? + + /// The number of the issue in the repository it belongs to + public let number: Int + + /// The state of the issue, open or closed + public let state: State + + /// The title of the issue + public let title: String + + /// The body of the issue + public let body: String + + /// The author of the issue + public let user: User? + + /// The labels associated to this issue, if any + public let labels: [Label] + + /// The user assigned to this issue, if any + public let assignees: [User] + + /// The milestone this issue belongs to, if any + public let milestone: Milestone? + + /// True if the issue has been closed by a contributor + public let locked: Bool + + /// The number of comments + public let commentCount: Int + + /// Contains the informations like the diff URL when the issue is a pull-request + public let pullRequest: PullRequest? + + /// The date this issue was closed at, if it ever were + public let closedAt: NSDate? + + /// The date this issue was created at + public let createdAt: NSDate + + /// The date this issue was updated at + public let updatedAt: NSDate + + public var hashValue: Int { + return ID.hashValue + } + + public var description: String { + return title + } + + public init(ID: String, URL: NSURL?, number: Int, state: State, title: String, body: String, user: User, labels: [Label], assignees: [User], milestone: Milestone?, locked: Bool, commentCount: Int, pullRequest: PullRequest?, closedAt: NSDate?, createdAt: NSDate, updatedAt: NSDate) { + self.ID = ID + self.URL = URL + self.number = number + self.state = state + self.title = title + self.body = body + self.user = user + self.milestone = milestone + self.locked = locked + self.commentCount = commentCount + self.pullRequest = pullRequest + self.labels = labels + self.assignees = assignees + self.closedAt = closedAt + self.createdAt = createdAt + self.updatedAt = updatedAt + } + +} + +public func ==(lhs: Issue, rhs: Issue) -> Bool { + return lhs.ID == rhs.ID + && lhs.URL == rhs.URL + && lhs.number == rhs.number + && lhs.state == rhs.state + && lhs.title == rhs.title + && lhs.body == rhs.body + && lhs.locked == rhs.locked + && lhs.commentCount == rhs.commentCount + && lhs.createdAt == rhs.createdAt + && lhs.updatedAt == rhs.updatedAt + && lhs.labels == rhs.labels + && lhs.milestone == rhs.milestone + && lhs.pullRequest == rhs.pullRequest +} + +extension Issue: ResourceType { + public static func decode(j: JSON) -> Decoded { + let f = curry(Issue.init) + + let closed_at: Decoded = (j <|? "closed_at").flatMap(toOptionalNSDate) + + return f + <^> (j <| "id" >>- toString) + <*> (j <| "html_url" >>- toNSURL) + <*> j <| "number" + <*> (j <| "state" >>- toIssueState) + <*> j <| "title" + <*> j <| "body" + <*> j <| "user" + <*> j <|| "labels" + <*> j <|| "assignees" + <*> j <|? "milestone" + <*> j <| "locked" + <*> j <| "comments" + <*> j <|? "pull_request" + <*> closed_at + <*> (j <| "created_at" >>- toNSDate) + <*> (j <| "updated_at" >>- toNSDate) + } +} diff --git a/Tentacle/Label.swift b/Tentacle/Label.swift new file mode 100644 index 0000000..2f1d17a --- /dev/null +++ b/Tentacle/Label.swift @@ -0,0 +1,37 @@ +// +// Label.swift +// Tentacle +// +// Created by Romain Pouclet on 2016-05-23. +// Copyright © 2016 Matt Diephouse. All rights reserved. +// + +import Foundation +import Argo +import Curry + +public struct Label: Hashable, CustomStringConvertible { + public let name: String + public let color: Color + + public var hashValue: Int { + return name.hashValue + } + + public var description: String { + return name + } +} + +public func ==(lhs: Label, rhs: Label) -> Bool { + return lhs.name == rhs.name +} + +extension Label: ResourceType { + public static func decode(json: JSON) -> Decoded