Skip to content

Commit 556099e

Browse files
authored
Merge pull request #372 from phadej/pr-324-addNotificationsEndpoint
Pr 324 add notifications endpoint
2 parents d8f87ff + 7020f6b commit 556099e

File tree

9 files changed

+293
-51
lines changed

9 files changed

+293
-51
lines changed

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
## Changes for next
22

3+
## Changes for 0.22
4+
5+
[#370](https://github.com/phadej/github/pull/370)
6+
- Type-class for various auth methods
7+
[#365](https://github.com/phadej/github/pull/365)
8+
- Throw on non-200 responses
9+
[#350](https://github.com/phadej/github/pull/350)
10+
- Add extension point for (preview) media types
11+
- Add missing webhook event types
12+
[#359](https://github.com/phadej/github/pull/359)
13+
- Add notifications endpoints
14+
[#324](https://github.com/phadej/github/pull/324)
15+
- Update dependencies
16+
[#364](https://github.com/phadej/github/pull/364)
17+
[#368](https://github.com/phadej/github/pull/368)
18+
[#369](https://github.com/phadej/github/pull/369)
19+
- Documentation improvements
20+
[#357](https://github.com/phadej/github/pull/357)
21+
322
## Changes for 0.21
423

524
- Refactor `Request` type.

github.cabal

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: >=1.10
22
name: github
3-
version: 0.21
3+
version: 0.22
44
synopsis: Access to the GitHub API, v3.
55
category: Network
66
description:
@@ -101,6 +101,7 @@ library
101101
GitHub.Data.Webhooks.Validate
102102
GitHub.Endpoints.Activity.Events
103103
GitHub.Endpoints.Activity.Starring
104+
GitHub.Endpoints.Activity.Notifications
104105
GitHub.Endpoints.Activity.Watching
105106
GitHub.Endpoints.Gists
106107
GitHub.Endpoints.Gists.Comments
@@ -206,6 +207,8 @@ test-suite github-test
206207
, bytestring
207208
, file-embed
208209
, github
210+
, tagged
211+
, text
209212
, hspec >=2.6.1 && <2.8
210213
, unordered-containers
211214
, vector

spec/GitHub/PullRequestsSpec.hs

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,103 @@
1-
{-# LANGUAGE OverloadedStrings #-}
2-
{-# LANGUAGE TemplateHaskell #-}
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE FlexibleInstances #-}
3+
{-# LANGUAGE MultiParamTypeClasses #-}
4+
{-# LANGUAGE OverloadedStrings #-}
5+
{-# LANGUAGE TemplateHaskell #-}
36
module GitHub.PullRequestsSpec where
47

5-
import qualified GitHub
8+
import qualified GitHub as GH
69

710
import Prelude ()
811
import Prelude.Compat
912

10-
import Data.Aeson (eitherDecodeStrict)
11-
import Data.ByteString (ByteString)
12-
import Data.Either.Compat (isRight)
13-
import Data.FileEmbed (embedFile)
14-
import Data.Foldable (for_)
15-
import Data.String (fromString)
16-
import qualified Data.Vector as V
13+
import Data.Aeson
14+
(FromJSON (..), eitherDecodeStrict, withObject, (.:))
15+
import Data.ByteString (ByteString)
1716
import qualified Data.ByteString.Lazy.Char8 as LBS8
18-
import System.Environment (lookupEnv)
17+
import Data.Either.Compat (isRight)
18+
import Data.FileEmbed (embedFile)
19+
import Data.Foldable (for_)
20+
import Data.String (fromString)
21+
import Data.Tagged (Tagged (..))
22+
import Data.Text (Text)
23+
import qualified Data.Vector as V
24+
import System.Environment (lookupEnv)
1925
import Test.Hspec
2026
(Spec, describe, it, pendingWith, shouldBe, shouldSatisfy)
2127

2228
fromRightS :: Show a => Either a b -> b
2329
fromRightS (Right b) = b
2430
fromRightS (Left a) = error $ "Expected a Right and got a Left" ++ show a
2531

26-
withAuth :: (GitHub.Auth -> IO ()) -> IO ()
32+
withAuth :: (GH.Auth -> IO ()) -> IO ()
2733
withAuth action = do
2834
mtoken <- lookupEnv "GITHUB_TOKEN"
2935
case mtoken of
3036
Nothing -> pendingWith "no GITHUB_TOKEN"
31-
Just token -> action (GitHub.OAuth $ fromString token)
37+
Just token -> action (GH.OAuth $ fromString token)
3238

3339
spec :: Spec
3440
spec = do
3541
describe "pullRequestsForR" $ do
3642
it "works" $ withAuth $ \auth -> for_ repos $ \(owner, repo) -> do
37-
cs <- GitHub.executeRequest auth $
38-
GitHub.pullRequestsForR owner repo opts GitHub.FetchAll
43+
cs <- GH.executeRequest auth $
44+
GH.pullRequestsForR owner repo opts GH.FetchAll
3945
cs `shouldSatisfy` isRight
4046

4147
describe "pullRequestPatchR" $
4248
it "works" $ withAuth $ \auth -> do
43-
Right patch <- GitHub.executeRequest auth $
44-
GitHub.pullRequestPatchR "phadej" "github" (GitHub.IssueNumber 349)
49+
Right patch <- GH.executeRequest auth $
50+
GH.pullRequestPatchR "phadej" "github" (GH.IssueNumber 349)
4551
head (LBS8.lines patch) `shouldBe` "From c0e4ad33811be82e1f72ee76116345c681703103 Mon Sep 17 00:00:00 2001"
4652

4753
describe "decoding pull request payloads" $ do
4854
it "decodes a pull request 'opened' payload" $ do
49-
V.length (GitHub.simplePullRequestRequestedReviewers simplePullRequestOpened)
55+
V.length (GH.simplePullRequestRequestedReviewers simplePullRequestOpened)
5056
`shouldBe` 0
5157

52-
V.length (GitHub.pullRequestRequestedReviewers pullRequestOpened)
58+
V.length (GH.pullRequestRequestedReviewers pullRequestOpened)
5359
`shouldBe` 0
5460

5561
it "decodes a pull request 'review_requested' payload" $ do
56-
V.length (GitHub.simplePullRequestRequestedReviewers simplePullRequestReviewRequested)
62+
V.length (GH.simplePullRequestRequestedReviewers simplePullRequestReviewRequested)
5763
`shouldBe` 1
5864

59-
V.length (GitHub.pullRequestRequestedReviewers pullRequestReviewRequested)
65+
V.length (GH.pullRequestRequestedReviewers pullRequestReviewRequested)
6066
`shouldBe` 1
6167

6268
describe "checking if a pull request is merged" $ do
6369
it "works" $ withAuth $ \auth -> do
64-
b <- GitHub.executeRequest auth $ GitHub.isPullRequestMergedR "phadej" "github" (GitHub.IssueNumber 14)
70+
b <- GH.executeRequest auth $ GH.isPullRequestMergedR "phadej" "github" (GH.IssueNumber 14)
6571
b `shouldSatisfy` isRight
6672
fromRightS b `shouldBe` True
6773

74+
describe "Draft Pull Request" $ do
75+
it "works" $ withAuth $ \auth -> do
76+
cs <- GH.executeRequest auth $
77+
draftPullRequestsForR "phadej" "github" opts GH.FetchAll
78+
79+
cs `shouldSatisfy` isRight
80+
6881
where
6982
repos =
7083
[ ("thoughtbot", "paperclip")
7184
, ("phadej", "github")
7285
]
73-
opts = GitHub.stateClosed
86+
opts = GH.stateClosed
7487

75-
simplePullRequestOpened :: GitHub.SimplePullRequest
88+
simplePullRequestOpened :: GH.SimplePullRequest
7689
simplePullRequestOpened =
7790
fromRightS (eitherDecodeStrict prOpenedPayload)
7891

79-
pullRequestOpened :: GitHub.PullRequest
92+
pullRequestOpened :: GH.PullRequest
8093
pullRequestOpened =
8194
fromRightS (eitherDecodeStrict prOpenedPayload)
8295

83-
simplePullRequestReviewRequested :: GitHub.SimplePullRequest
96+
simplePullRequestReviewRequested :: GH.SimplePullRequest
8497
simplePullRequestReviewRequested =
8598
fromRightS (eitherDecodeStrict prReviewRequestedPayload)
8699

87-
pullRequestReviewRequested :: GitHub.PullRequest
100+
pullRequestReviewRequested :: GH.PullRequest
88101
pullRequestReviewRequested =
89102
fromRightS (eitherDecodeStrict prReviewRequestedPayload)
90103

@@ -93,3 +106,41 @@ spec = do
93106

94107
prReviewRequestedPayload :: ByteString
95108
prReviewRequestedPayload = $(embedFile "fixtures/pull-request-review-requested.json")
109+
110+
-------------------------------------------------------------------------------
111+
-- Draft Pull Requests
112+
-------------------------------------------------------------------------------
113+
114+
draftPullRequestsForR
115+
:: GH.Name GH.Owner
116+
-> GH.Name GH.Repo
117+
-> GH.PullRequestMod
118+
-> GH.FetchCount
119+
-> GH.GenRequest ('GH.MtPreview ShadowCat) k (V.Vector DraftPR)
120+
draftPullRequestsForR user repo opts = GH.PagedQuery
121+
["repos", GH.toPathPart user, GH.toPathPart repo, "pulls"]
122+
(GH.prModToQueryString opts)
123+
124+
data DraftPR = DraftPR
125+
{ dprId :: !(GH.Id GH.PullRequest)
126+
, dprNumber :: !GH.IssueNumber
127+
, dprTitle :: !Text
128+
, dprDraft :: !Bool
129+
}
130+
deriving (Show)
131+
132+
instance FromJSON DraftPR where
133+
parseJSON = withObject "DraftPR" $ \obj -> DraftPR
134+
<$> obj .: "id"
135+
<*> obj .: "number"
136+
<*> obj .: "title"
137+
<*> obj .: "draft"
138+
139+
-- | @application/vnd.github.shadow-cat-preview+json@ <https://developer.github.com/v3/previews/#draft-pull-requests>
140+
data ShadowCat
141+
142+
instance GH.PreviewAccept ShadowCat where
143+
previewContentType = Tagged "application/vnd.github.shadow-cat-preview+json"
144+
145+
instance FromJSON a => GH.PreviewParseResponse ShadowCat a where
146+
previewParseResponse _ res = Tagged (GH.parseResponseJSON res)

src/GitHub.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,16 @@ module GitHub (
1616
-- | See <https://developer.github.com/v3/activity/>
1717

1818
-- ** Events
19-
-- | See https://developer.github.com/v3/activity/events/#events
19+
-- | See <https://developer.github.com/v3/activity/events/>
2020
repositoryEventsR,
2121
userEventsR,
22+
23+
-- ** Notifications
24+
-- | See <https://developer.github.com/v3/activity/notifications/>
25+
getNotificationsR,
26+
markNotificationAsReadR,
27+
markAllNotificationsAsReadR,
28+
2229
-- ** Starring
2330
-- | See <https://developer.github.com/v3/activity/starring/>
2431
--
@@ -374,6 +381,7 @@ module GitHub (
374381

375382
import GitHub.Data
376383
import GitHub.Endpoints.Activity.Events
384+
import GitHub.Endpoints.Activity.Notifications
377385
import GitHub.Endpoints.Activity.Starring
378386
import GitHub.Endpoints.Activity.Watching
379387
import GitHub.Endpoints.Gists

src/GitHub/Data/Activities.hs

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
--
66
module GitHub.Data.Activities where
77

8-
import GitHub.Data.Repos (Repo)
8+
import GitHub.Data.Id (Id, mkId)
9+
import GitHub.Data.Repos (Repo, RepoRef)
10+
import GitHub.Data.URL (URL)
911
import GitHub.Internal.Prelude
12+
1013
import Prelude ()
1114

1215
data RepoStarred = RepoStarred
@@ -24,3 +27,82 @@ instance FromJSON RepoStarred where
2427
<$> o .: "starred_at"
2528
<*> o .: "repo"
2629

30+
data Subject = Subject
31+
{ subjectTitle :: !Text
32+
, subjectURL :: !URL
33+
, subjectLatestCommentURL :: !(Maybe URL)
34+
-- https://developer.github.com/v3/activity/notifications/ doesn't indicate
35+
-- what the possible values for this field are.
36+
-- TODO: Make an ADT for this.
37+
, subjectType :: !Text
38+
}
39+
deriving (Show, Data, Typeable, Eq, Ord, Generic)
40+
41+
instance NFData Subject where rnf = genericRnf
42+
instance Binary Subject
43+
44+
instance FromJSON Subject where
45+
parseJSON = withObject "Subject" $ \o -> Subject
46+
<$> o .: "title"
47+
<*> o .: "url"
48+
<*> o .:? "latest_comment_url"
49+
<*> o .: "type"
50+
51+
data NotificationReason
52+
= AssignReason
53+
| AuthorReason
54+
| CommentReason
55+
| InvitationReason
56+
| ManualReason
57+
| MentionReason
58+
| ReviewRequestedReason
59+
| StateChangeReason
60+
| SubscribedReason
61+
| TeamMentionReason
62+
deriving (Show, Data, Enum, Bounded, Typeable, Eq, Ord, Generic)
63+
64+
instance NFData NotificationReason where rnf = genericRnf
65+
instance Binary NotificationReason
66+
67+
instance FromJSON NotificationReason where
68+
parseJSON = withText "NotificationReason" $ \t -> case t of
69+
"assign" -> pure AssignReason
70+
"author" -> pure AuthorReason
71+
"comment" -> pure CommentReason
72+
"invitation" -> pure InvitationReason
73+
"manual" -> pure ManualReason
74+
"mention" -> pure MentionReason
75+
"review_requested" -> pure ReviewRequestedReason
76+
"state_change" -> pure StateChangeReason
77+
"subscribed" -> pure SubscribedReason
78+
"team_mention" -> pure TeamMentionReason
79+
_ -> fail $ "Unknown NotificationReason " ++ show t
80+
81+
data Notification = Notification
82+
-- XXX: The notification id field type IS in fact string. Not sure why gh
83+
-- chose to do this when all the other ids are Numbers...
84+
{ notificationId :: !(Id Notification)
85+
, notificationRepo :: !RepoRef
86+
, notificationSubject :: !Subject
87+
, notificationReason :: !NotificationReason
88+
, notificationUnread :: !Bool
89+
, notificationUpdatedAt :: !(Maybe UTCTime)
90+
, notificationLastReadAt :: !(Maybe UTCTime)
91+
, notificationUrl :: !URL
92+
}
93+
deriving (Show, Data, Typeable, Eq, Ord, Generic)
94+
95+
instance NFData Notification where rnf = genericRnf
96+
instance Binary Notification
97+
98+
instance FromJSON Notification where
99+
parseJSON = withObject "Notification" $ \o -> Notification
100+
<$> (mkId undefined . read <$> o .: "id")
101+
<*> o .: "repository"
102+
<*> o .: "subject"
103+
<*> o .: "reason"
104+
<*> o .: "unread"
105+
<*> o .: "updated_at"
106+
<*> o .: "last_read_at"
107+
<*> o .: "url"
108+

src/GitHub/Data/Request.hs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,18 @@ instance NFData FetchCount where rnf = genericRnf
105105
-- MediaType
106106
-------------------------------------------------------------------------------
107107

108-
data MediaType
109-
= MtJSON -- ^ @application/vnd.github.v3+json@
110-
| MtRaw -- ^ @application/vnd.github.v3.raw@ <https://developer.github.com/v3/media/#raw-1>
111-
| MtDiff -- ^ @application/vnd.github.v3.diff@ <https://developer.github.com/v3/media/#diff>
112-
| MtPatch -- ^ @application/vnd.github.v3.patch@ <https://developer.github.com/v3/media/#patch>
113-
| MtSha -- ^ @application/vnd.github.v3.sha@ <https://developer.github.com/v3/media/#sha>
114-
| MtStar -- ^ @application/vnd.github.v3.star+json@ <https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps-1>
115-
| MtRedirect -- ^ <https://developer.github.com/v3/repos/contents/#get-archive-link>
116-
| MtStatus -- ^ Parse status
117-
| MtUnit -- ^ Always succeeds
118-
deriving (Eq, Ord, Read, Show, Enum, Bounded, Typeable, Data, Generic)
108+
data MediaType a
109+
= MtJSON -- ^ @application/vnd.github.v3+json@
110+
| MtRaw -- ^ @application/vnd.github.v3.raw@ <https://developer.github.com/v3/media/#raw-1>
111+
| MtDiff -- ^ @application/vnd.github.v3.diff@ <https://developer.github.com/v3/media/#diff>
112+
| MtPatch -- ^ @application/vnd.github.v3.patch@ <https://developer.github.com/v3/media/#patch>
113+
| MtSha -- ^ @application/vnd.github.v3.sha@ <https://developer.github.com/v3/media/#sha>
114+
| MtStar -- ^ @application/vnd.github.v3.star+json@ <https://developer.github.com/v3/activity/starring/#alternative-response-with-star-creation-timestamps-1>
115+
| MtRedirect -- ^ <https://developer.github.com/v3/repos/contents/#get-archive-link>
116+
| MtStatus -- ^ Parse status
117+
| MtUnit -- ^ Always succeeds
118+
| MtPreview a -- ^ Some other (preview) type; this is an extension point.
119+
deriving (Eq, Ord, Read, Show, Typeable, Data, Generic)
119120

120121
------------------------------------------------------------------------------
121122
-- RW
@@ -151,7 +152,7 @@ instance IReadOnly 'RA where iro = ROA
151152
-- * @a@ is the result type
152153
--
153154
-- /Note:/ 'Request' is not 'Functor' on purpose.
154-
data GenRequest (mt :: MediaType) (rw :: RW) a where
155+
data GenRequest (mt :: MediaType *) (rw :: RW) a where
155156
Query :: Paths -> QueryString -> GenRequest mt rw a
156157
PagedQuery :: Paths -> QueryString -> FetchCount -> GenRequest mt rw (Vector a)
157158

0 commit comments

Comments
 (0)