github-post-receive 1.0.2.0 → 1.1.0.0
raw patch · 3 files changed
+398/−65 lines, 3 filesdep +containersdep +wai-loggerdep −conduitdep ~waidep ~warpPVP ok
version bump matches the API change (PVP)
Dependencies added: containers, wai-logger
Dependencies removed: conduit
Dependency ranges changed: wai, warp
API changes (from Hackage documentation)
- Github.PostReceive.Types: Payload :: Text -> Text -> Text -> Bool -> Bool -> Bool -> Text -> [Commit] -> Commit -> Repository -> User -> Payload
- Github.PostReceive.Types: payloadAfter :: Payload -> Text
- Github.PostReceive.Types: payloadBefore :: Payload -> Text
- Github.PostReceive.Types: payloadCommits :: Payload -> [Commit]
- Github.PostReceive.Types: payloadCompare :: Payload -> Text
- Github.PostReceive.Types: payloadCreated :: Payload -> Bool
- Github.PostReceive.Types: payloadDeleted :: Payload -> Bool
- Github.PostReceive.Types: payloadForced :: Payload -> Bool
- Github.PostReceive.Types: payloadHeadCommit :: Payload -> Commit
- Github.PostReceive.Types: payloadPusher :: Payload -> User
- Github.PostReceive.Types: payloadRef :: Payload -> Text
- Github.PostReceive.Types: payloadRepository :: Payload -> Repository
- Github.PostReceive.Types: userEmail :: User -> Maybe EmailAddress
- Github.PostReceive.Types: userName :: User -> Text
- Github.PostReceive.Types: userUsername :: User -> Maybe Text
+ Github.PostReceive.Server: app :: ApacheLogger -> Map ByteString (Payload -> IO ()) -> Application
+ Github.PostReceive.Types: Branch :: Text -> SimpleCommit -> Branch
+ Github.PostReceive.Types: L :: a -> Or a b
+ Github.PostReceive.Types: Push :: PushEvent -> Payload
+ Github.PostReceive.Types: PushEvent :: Text -> Text -> Text -> Bool -> Bool -> Bool -> Maybe Text -> Url -> [Commit] -> Commit -> Repository -> SimpleUser -> User -> PushEvent
+ Github.PostReceive.Types: R :: b -> Or a b
+ Github.PostReceive.Types: SimpleCommit :: Text -> Url -> Maybe Url -> SimpleCommit
+ Github.PostReceive.Types: SimpleStatusCommit :: SimpleUser -> SimpleUser -> Text -> Tree -> Url -> Int -> SimpleStatusCommit
+ Github.PostReceive.Types: SimpleUser :: Text -> Maybe EmailAddress -> Maybe Text -> Maybe Text -> SimpleUser
+ Github.PostReceive.Types: Status :: StatusEvent -> Payload
+ Github.PostReceive.Types: StatusCommit :: Text -> SimpleStatusCommit -> Url -> Url -> Url -> User -> User -> [SimpleCommit] -> StatusCommit
+ Github.PostReceive.Types: StatusEvent :: Int -> Text -> Text -> Url -> Text -> Text -> Text -> StatusCommit -> [Branch] -> Text -> Text -> Repository -> User -> StatusEvent
+ Github.PostReceive.Types: Tree :: Text -> Url -> Tree
+ Github.PostReceive.Types: branchCommit :: Branch -> SimpleCommit
+ Github.PostReceive.Types: branchName :: Branch -> Text
+ Github.PostReceive.Types: data Branch
+ Github.PostReceive.Types: data Or a b
+ Github.PostReceive.Types: data PushEvent
+ Github.PostReceive.Types: data SimpleCommit
+ Github.PostReceive.Types: data SimpleStatusCommit
+ Github.PostReceive.Types: data SimpleUser
+ Github.PostReceive.Types: data StatusCommit
+ Github.PostReceive.Types: data StatusEvent
+ Github.PostReceive.Types: data Tree
+ Github.PostReceive.Types: instance (Eq a, Eq b) => Eq (Or a b)
+ Github.PostReceive.Types: instance (FromJSON a, FromJSON b) => FromJSON (Or a b)
+ Github.PostReceive.Types: instance (Show a, Show b) => Show (Or a b)
+ Github.PostReceive.Types: instance Eq Branch
+ Github.PostReceive.Types: instance Eq PushEvent
+ Github.PostReceive.Types: instance Eq SimpleCommit
+ Github.PostReceive.Types: instance Eq SimpleStatusCommit
+ Github.PostReceive.Types: instance Eq SimpleUser
+ Github.PostReceive.Types: instance Eq StatusCommit
+ Github.PostReceive.Types: instance Eq StatusEvent
+ Github.PostReceive.Types: instance Eq Tree
+ Github.PostReceive.Types: instance FromJSON Branch
+ Github.PostReceive.Types: instance FromJSON PushEvent
+ Github.PostReceive.Types: instance FromJSON SimpleCommit
+ Github.PostReceive.Types: instance FromJSON SimpleStatusCommit
+ Github.PostReceive.Types: instance FromJSON SimpleUser
+ Github.PostReceive.Types: instance FromJSON StatusCommit
+ Github.PostReceive.Types: instance FromJSON StatusEvent
+ Github.PostReceive.Types: instance FromJSON Tree
+ Github.PostReceive.Types: instance Show Branch
+ Github.PostReceive.Types: instance Show PushEvent
+ Github.PostReceive.Types: instance Show SimpleCommit
+ Github.PostReceive.Types: instance Show SimpleStatusCommit
+ Github.PostReceive.Types: instance Show SimpleUser
+ Github.PostReceive.Types: instance Show StatusCommit
+ Github.PostReceive.Types: instance Show StatusEvent
+ Github.PostReceive.Types: instance Show Tree
+ Github.PostReceive.Types: instance Typeable Branch
+ Github.PostReceive.Types: instance Typeable Or
+ Github.PostReceive.Types: instance Typeable PushEvent
+ Github.PostReceive.Types: instance Typeable SimpleCommit
+ Github.PostReceive.Types: instance Typeable SimpleStatusCommit
+ Github.PostReceive.Types: instance Typeable SimpleUser
+ Github.PostReceive.Types: instance Typeable StatusCommit
+ Github.PostReceive.Types: instance Typeable StatusEvent
+ Github.PostReceive.Types: instance Typeable Tree
+ Github.PostReceive.Types: pushEventAfter :: PushEvent -> Text
+ Github.PostReceive.Types: pushEventBaseRef :: PushEvent -> Maybe Text
+ Github.PostReceive.Types: pushEventBefore :: PushEvent -> Text
+ Github.PostReceive.Types: pushEventCommits :: PushEvent -> [Commit]
+ Github.PostReceive.Types: pushEventCompare :: PushEvent -> Url
+ Github.PostReceive.Types: pushEventCreated :: PushEvent -> Bool
+ Github.PostReceive.Types: pushEventDeleted :: PushEvent -> Bool
+ Github.PostReceive.Types: pushEventForced :: PushEvent -> Bool
+ Github.PostReceive.Types: pushEventHeadCommit :: PushEvent -> Commit
+ Github.PostReceive.Types: pushEventPusher :: PushEvent -> SimpleUser
+ Github.PostReceive.Types: pushEventRef :: PushEvent -> Text
+ Github.PostReceive.Types: pushEventRepository :: PushEvent -> Repository
+ Github.PostReceive.Types: pushEventSender :: PushEvent -> User
+ Github.PostReceive.Types: repoArchiveUrl :: Repository -> Url
+ Github.PostReceive.Types: repoAssigneesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoBlobsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoBranchesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoCloneUrl :: Repository -> Url
+ Github.PostReceive.Types: repoCollaboratorsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoCommitsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoCompareUrl :: Repository -> Url
+ Github.PostReceive.Types: repoContentsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoContributorsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoDownloadsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoEventsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoForksCount :: Repository -> Int
+ Github.PostReceive.Types: repoForksUrl :: Repository -> Url
+ Github.PostReceive.Types: repoFullName :: Repository -> Text
+ Github.PostReceive.Types: repoGitCommitsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoGitRefsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoGitTagsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoGitUrl :: Repository -> Url
+ Github.PostReceive.Types: repoHasPages :: Repository -> Bool
+ Github.PostReceive.Types: repoHooksUrl :: Repository -> Url
+ Github.PostReceive.Types: repoHtmlUrl :: Repository -> Url
+ Github.PostReceive.Types: repoIssueCommentUrl :: Repository -> Url
+ Github.PostReceive.Types: repoIssueEventsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoIssuesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoKeysUrl :: Repository -> Url
+ Github.PostReceive.Types: repoLabelsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoLanguagesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoMergesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoMilestonesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoMirrorUrl :: Repository -> Maybe Url
+ Github.PostReceive.Types: repoNotificationsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoOpenIssuesCount :: Repository -> Int
+ Github.PostReceive.Types: repoPullsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoReleasesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoSshUrl :: Repository -> Url
+ Github.PostReceive.Types: repoStargazersCount :: Repository -> Int
+ Github.PostReceive.Types: repoStargazersUrl :: Repository -> Url
+ Github.PostReceive.Types: repoStatusesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoSubscribersUrl :: Repository -> Url
+ Github.PostReceive.Types: repoSubscriptionUrl :: Repository -> Url
+ Github.PostReceive.Types: repoSvnUrl :: Repository -> Url
+ Github.PostReceive.Types: repoTagsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoTeamsUrl :: Repository -> Url
+ Github.PostReceive.Types: repoTreesUrl :: Repository -> Url
+ Github.PostReceive.Types: repoUpdatedAt :: Repository -> Text
+ Github.PostReceive.Types: repoWatchersCount :: Repository -> Int
+ Github.PostReceive.Types: simpleCommitHtmlUrl :: SimpleCommit -> Maybe Url
+ Github.PostReceive.Types: simpleCommitSha :: SimpleCommit -> Text
+ Github.PostReceive.Types: simpleCommitUrl :: SimpleCommit -> Url
+ Github.PostReceive.Types: simpleStatusCommitAuthor :: SimpleStatusCommit -> SimpleUser
+ Github.PostReceive.Types: simpleStatusCommitCommentCount :: SimpleStatusCommit -> Int
+ Github.PostReceive.Types: simpleStatusCommitCommitter :: SimpleStatusCommit -> SimpleUser
+ Github.PostReceive.Types: simpleStatusCommitMessage :: SimpleStatusCommit -> Text
+ Github.PostReceive.Types: simpleStatusCommitTree :: SimpleStatusCommit -> Tree
+ Github.PostReceive.Types: simpleStatusCommitUrl :: SimpleStatusCommit -> Url
+ Github.PostReceive.Types: simpleUserDate :: SimpleUser -> Maybe Text
+ Github.PostReceive.Types: simpleUserEmail :: SimpleUser -> Maybe EmailAddress
+ Github.PostReceive.Types: simpleUserName :: SimpleUser -> Text
+ Github.PostReceive.Types: simpleUserUsername :: SimpleUser -> Maybe Text
+ Github.PostReceive.Types: statusCommitAuthor :: StatusCommit -> User
+ Github.PostReceive.Types: statusCommitCommentsUrl :: StatusCommit -> Url
+ Github.PostReceive.Types: statusCommitCommit :: StatusCommit -> SimpleStatusCommit
+ Github.PostReceive.Types: statusCommitCommitter :: StatusCommit -> User
+ Github.PostReceive.Types: statusCommitHtmlUrl :: StatusCommit -> Url
+ Github.PostReceive.Types: statusCommitParents :: StatusCommit -> [SimpleCommit]
+ Github.PostReceive.Types: statusCommitSHA :: StatusCommit -> Text
+ Github.PostReceive.Types: statusCommitUrl :: StatusCommit -> Url
+ Github.PostReceive.Types: statusEventBranches :: StatusEvent -> [Branch]
+ Github.PostReceive.Types: statusEventCommit :: StatusEvent -> StatusCommit
+ Github.PostReceive.Types: statusEventContext :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventCreatedAt :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventDescription :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventId :: StatusEvent -> Int
+ Github.PostReceive.Types: statusEventName :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventRepository :: StatusEvent -> Repository
+ Github.PostReceive.Types: statusEventSHA :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventSender :: StatusEvent -> User
+ Github.PostReceive.Types: statusEventState :: StatusEvent -> Text
+ Github.PostReceive.Types: statusEventTargetUrl :: StatusEvent -> Url
+ Github.PostReceive.Types: statusEventUpdatedAt :: StatusEvent -> Text
+ Github.PostReceive.Types: toEither :: Or a b -> Either a b
+ Github.PostReceive.Types: treeSHA :: Tree -> Text
+ Github.PostReceive.Types: treeUrl :: Tree -> Url
+ Github.PostReceive.Types: userAvatarUrl :: User -> Url
+ Github.PostReceive.Types: userEventsUrl :: User -> Url
+ Github.PostReceive.Types: userFollowersUrl :: User -> Url
+ Github.PostReceive.Types: userFollowingUrl :: User -> Url
+ Github.PostReceive.Types: userGistsUrl :: User -> Url
+ Github.PostReceive.Types: userGravatarId :: User -> Text
+ Github.PostReceive.Types: userHtmlUrl :: User -> Url
+ Github.PostReceive.Types: userId :: User -> Int
+ Github.PostReceive.Types: userLogin :: User -> Text
+ Github.PostReceive.Types: userOrganizationsUrl :: User -> Url
+ Github.PostReceive.Types: userReceivedEventsUrl :: User -> Url
+ Github.PostReceive.Types: userReposUrl :: User -> Url
+ Github.PostReceive.Types: userSiteAdmin :: User -> Bool
+ Github.PostReceive.Types: userStarredUrl :: User -> Url
+ Github.PostReceive.Types: userSubscriptionsUrl :: User -> Url
+ Github.PostReceive.Types: userType :: User -> Text
+ Github.PostReceive.Types: userUrl :: User -> Url
- Github.PostReceive.Server: start :: Port -> (Payload -> IO ()) -> IO ()
+ Github.PostReceive.Server: start :: Port -> Map ByteString (Payload -> IO ()) -> IO ()
- Github.PostReceive.Types: Commit :: Text -> Bool -> Text -> Text -> Text -> User -> User -> [FilePath] -> [FilePath] -> [FilePath] -> Commit
+ Github.PostReceive.Types: Commit :: Text -> Bool -> Text -> Text -> Url -> SimpleUser -> SimpleUser -> [FilePath] -> [FilePath] -> [FilePath] -> Commit
- Github.PostReceive.Types: Repository :: Int -> Text -> Text -> Text -> Maybe Text -> Int -> Int -> Int -> Bool -> Int -> User -> Bool -> Int -> Bool -> Bool -> Bool -> Text -> Int -> Int -> Text -> Repository
+ Github.PostReceive.Types: Repository :: Int -> Text -> Text -> Or SimpleUser User -> Bool -> Url -> Text -> Bool -> Text -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Or Int Text -> Text -> Or Int Text -> Url -> Url -> Url -> Url -> Maybe Url -> Int -> Int -> Int -> Text -> Bool -> Bool -> Bool -> Bool -> Int -> Maybe Url -> Int -> Int -> Int -> Int -> Maybe Int -> Maybe Text -> Repository
- Github.PostReceive.Types: User :: Text -> Maybe EmailAddress -> Maybe Text -> User
+ Github.PostReceive.Types: User :: Text -> Int -> Url -> Text -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Url -> Text -> Bool -> User
- Github.PostReceive.Types: commitAuthor :: Commit -> User
+ Github.PostReceive.Types: commitAuthor :: Commit -> SimpleUser
- Github.PostReceive.Types: commitCommitter :: Commit -> User
+ Github.PostReceive.Types: commitCommitter :: Commit -> SimpleUser
- Github.PostReceive.Types: commitUrl :: Commit -> Text
+ Github.PostReceive.Types: commitUrl :: Commit -> Url
- Github.PostReceive.Types: repoCreatedAt :: Repository -> Int
+ Github.PostReceive.Types: repoCreatedAt :: Repository -> Or Int Text
- Github.PostReceive.Types: repoHomepage :: Repository -> Maybe Text
+ Github.PostReceive.Types: repoHomepage :: Repository -> Maybe Url
- Github.PostReceive.Types: repoMasterBranch :: Repository -> Text
+ Github.PostReceive.Types: repoMasterBranch :: Repository -> Maybe Text
- Github.PostReceive.Types: repoOwner :: Repository -> User
+ Github.PostReceive.Types: repoOwner :: Repository -> Or SimpleUser User
- Github.PostReceive.Types: repoPushedAt :: Repository -> Int
+ Github.PostReceive.Types: repoPushedAt :: Repository -> Or Int Text
- Github.PostReceive.Types: repoStargazers :: Repository -> Int
+ Github.PostReceive.Types: repoStargazers :: Repository -> Maybe Int
Files
- github-post-receive.cabal +13/−6
- src/Github/PostReceive/Server.hs +50/−11
- src/Github/PostReceive/Types.hs +335/−48
github-post-receive.cabal view
@@ -1,7 +1,13 @@ name: github-post-receive-version: 1.0.2.0-synopsis: Github post-receive server library-description: This is a library to construct github post-receive servers.+version: 1.1.0.0+synopsis: GitHub webhooks library+description: This is a library to construct github webhooks servers.+ .+ Currently, this library supports following events:+ .+ * push+ .+ * status homepage: https://github.com/amutake/github-post-receive license: BSD3 license-file: LICENSE@@ -24,11 +30,12 @@ build-depends: base ==4.* , bytestring >=0.10 , text >=0.11+ , containers , aeson >=0.6.1 , email-validate >=1.0- , conduit >=1.0 , http-types >=0.8- , wai ==2.*- , warp ==2.*+ , wai ==3.*+ , warp >=3.0.1 && < 4+ , wai-logger ==2.* ghc-options: -Wall -fno-warn-orphans -fno-warn-unused-do-bind default-language: Haskell2010
src/Github/PostReceive/Server.hs view
@@ -1,22 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}+ module Github.PostReceive.Server ( start+ , app ) where import Control.Applicative ((<$>))-import Data.Aeson (decode)+import Data.Aeson (eitherDecode) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL-import Data.Conduit (($$))-import qualified Data.Conduit.List as CL-import Network.HTTP.Types (ok200, badRequest400, urlDecode)-import Network.Wai (requestBody, responseLBS)+import qualified Data.Map as M+import Network.HTTP.Types+ ( ok200, badRequest400, notFound404, internalServerError500+ , urlDecode, methodPost, hContentType+ )+import Network.Wai+ ( strictRequestBody, responseLBS, Application+ , rawPathInfo, requestMethod, requestHeaders+ ) import Network.Wai.Handler.Warp (run, Port)+import Network.Wai.Logger (withStdoutLogger, ApacheLogger) import Github.PostReceive.Types (Payload) -start :: Port -> (Payload -> IO ()) -> IO ()-start port cont = run port $ \req -> do- bs <- B.drop (length "payload=") . B.concat <$> (requestBody req $$ CL.consume)- case decode $ BL.fromStrict $ urlDecode True bs of- Nothing -> return (responseLBS badRequest400 [] BL.empty)- Just payload -> cont payload >> return (responseLBS ok200 [] BL.empty)+start :: Port -> M.Map B.ByteString (Payload -> IO ()) -> IO ()+start port routes = do+ putStrLn startingMessage+ withStdoutLogger $ run port . flip app routes+ where+ startingMessage = concat+ [ "github-post-receive listening on port "+ , show port+ , " with path "+ , show $ M.keys routes+ ]++app :: ApacheLogger -> M.Map B.ByteString (Payload -> IO ()) -> Application+app aplogger routes req respond+ | method == methodPost = flip (maybe notFound) (M.lookup path routes) $ \cont ->+ case contentType of+ Just "application/json" -> jsonCase cont+ Just "application/x-www-form-urlencoded" -> formCase cont+ _ -> badRequest+ | otherwise = notFound+ where+ path = rawPathInfo req+ method = requestMethod req+ contentType = lookup hContentType $ requestHeaders req+ res status = aplogger req status Nothing >> respond (responseLBS status [] BL.empty)+ notFound = res notFound404+ badRequest = res badRequest400+ internalError reason = putStrLn reason >> res internalServerError500+ ok = res ok200+ jsonCase cont = do+ bs <- strictRequestBody req+ flip (either internalError) (eitherDecode bs) $ \payload -> cont payload >> ok+ formCase cont = do+ bs <- BL.drop (BL.length "payload=") <$> strictRequestBody req+ flip (either internalError) (eitherDecode $ BL.fromStrict $ urlDecode True $ BL.toStrict bs) $ \payload ->+ cont payload >> ok
src/Github/PostReceive/Types.hs view
@@ -1,15 +1,26 @@ {-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Github.PostReceive.Types ( Payload (..)+ , PushEvent (..)+ , StatusEvent (..) , Commit (..) , Repository (..) , User (..)+ , SimpleUser (..)+ , Branch (..)+ , SimpleCommit (..)+ , StatusCommit (..)+ , SimpleStatusCommit (..)+ , Tree (..)+ , Or (..)+ , toEither -- Re-exports , EmailAddress ) where -import Control.Applicative ((<$>), (<*>), pure)+import Control.Applicative ((<$>), (<*>), pure, (<|>)) import Data.Aeson (Value (..), FromJSON (..), (.:), (.:?)) import qualified Data.ByteString.Char8 as B import Data.Text (Text)@@ -17,43 +28,88 @@ import Data.Typeable (Typeable) import Text.Email.Validate (EmailAddress, emailAddress) -data Payload = Payload- { payloadRef :: Text- , payloadAfter :: Text- , payloadBefore :: Text- , payloadCreated :: Bool- , payloadDeleted :: Bool- , payloadForced :: Bool- , payloadCompare :: Text- , payloadCommits :: [Commit]- , payloadHeadCommit :: Commit- , payloadRepository :: Repository- , payloadPusher :: User- } deriving (Show, Eq, Typeable)+data Payload = Push PushEvent+ | Status StatusEvent+ deriving (Show, Eq, Typeable) instance FromJSON Payload where- parseJSON (Object o) = Payload+ parseJSON v = Push <$> parseJSON v+ <|> Status <$> parseJSON v++data PushEvent = PushEvent+ { pushEventRef :: Text+ , pushEventBefore :: Text+ , pushEventAfter :: Text+ , pushEventCreated :: Bool+ , pushEventDeleted :: Bool+ , pushEventForced :: Bool+ , pushEventBaseRef :: Maybe Text+ , pushEventCompare :: Url+ , pushEventCommits :: [Commit]+ , pushEventHeadCommit :: Commit+ , pushEventRepository :: Repository+ , pushEventPusher :: SimpleUser+ , pushEventSender :: User+ } deriving (Show, Eq, Typeable)++instance FromJSON PushEvent where+ parseJSON (Object o) = PushEvent <$> o .: "ref"- <*> o .: "after" <*> o .: "before"+ <*> o .: "after" <*> o .: "created" <*> o .: "deleted" <*> o .: "forced"+ <*> o .:? "base_ref" <*> o .: "compare" <*> o .: "commits" <*> o .: "head_commit" <*> o .: "repository" <*> o .: "pusher"- parseJSON _ = fail "Payload must be an object"+ <*> o .: "sender"+ parseJSON _ = fail "PushEvent must be an object" +data StatusEvent = StatusEvent+ { statusEventId :: Int+ , statusEventSHA :: Text+ , statusEventName :: Text+ , statusEventTargetUrl :: Url+ , statusEventContext :: Text+ , statusEventDescription :: Text+ , statusEventState :: Text+ , statusEventCommit :: StatusCommit+ , statusEventBranches :: [Branch]+ , statusEventCreatedAt :: Text -- TODO: Change to date type+ , statusEventUpdatedAt :: Text -- TODO: Change to date type+ , statusEventRepository :: Repository+ , statusEventSender :: User+ } deriving (Show, Eq, Typeable)++instance FromJSON StatusEvent where+ parseJSON (Object o) = StatusEvent+ <$> o .: "id"+ <*> o .: "sha"+ <*> o .: "name"+ <*> o .: "target_url"+ <*> o .: "context"+ <*> o .: "description"+ <*> o .: "state"+ <*> o .: "commit"+ <*> o .: "branches"+ <*> o .: "created_at"+ <*> o .: "updated_at"+ <*> o .: "repository"+ <*> o .: "sender"+ parseJSON _ = fail "StatusEvent must be an object"+ data Commit = Commit { commitId :: Text , commitDistinct :: Bool , commitMessage :: Text , commitTimestamp :: Text- , commitUrl :: Text- , commitAuthor :: User- , commitCommitter :: User+ , commitUrl :: Url+ , commitAuthor :: SimpleUser+ , commitCommitter :: SimpleUser , commitAdded :: [FilePath] , commitRemoved :: [FilePath] , commitModified :: [FilePath]@@ -76,65 +132,296 @@ data Repository = Repository { repoId :: Int , repoName :: Text- , repoUrl :: Text+ , repoFullName :: Text+ , repoOwner :: Or SimpleUser User+ , repoPrivate :: Bool+ , repoHtmlUrl :: Url , repoDescription :: Text- , repoHomepage :: Maybe Text- , repoWatchers :: Int- , repoStargazers :: Int- , repoForks :: Int , repoFork :: Bool+ -- urls+ , repoUrl :: Text+ , repoForksUrl :: Url+ , repoKeysUrl :: Url+ , repoCollaboratorsUrl :: Url+ , repoTeamsUrl :: Url+ , repoHooksUrl :: Url+ , repoIssueEventsUrl :: Url+ , repoEventsUrl :: Url+ , repoAssigneesUrl :: Url+ , repoBranchesUrl :: Url+ , repoTagsUrl :: Url+ , repoBlobsUrl :: Url+ , repoGitTagsUrl :: Url+ , repoGitRefsUrl :: Url+ , repoTreesUrl :: Url+ , repoStatusesUrl :: Url+ , repoLanguagesUrl :: Url+ , repoStargazersUrl :: Url+ , repoContributorsUrl :: Url+ , repoSubscribersUrl :: Url+ , repoSubscriptionUrl :: Url+ , repoCommitsUrl :: Url+ , repoGitCommitsUrl :: Url+ , repoIssueCommentUrl :: Url+ , repoContentsUrl :: Url+ , repoCompareUrl :: Url+ , repoMergesUrl :: Url+ , repoArchiveUrl :: Url+ , repoDownloadsUrl :: Url+ , repoIssuesUrl :: Url+ , repoPullsUrl :: Url+ , repoMilestonesUrl :: Url+ , repoNotificationsUrl :: Url+ , repoLabelsUrl :: Url+ , repoReleasesUrl :: Url+ -- date+ , repoCreatedAt :: Or Int Text -- Int or DateString+ , repoUpdatedAt :: Text+ , repoPushedAt :: Or Int Text -- Int or DateString+ , repoGitUrl :: Url+ , repoSshUrl :: Url+ , repoCloneUrl :: Url+ , repoSvnUrl :: Url+ , repoHomepage :: Maybe Url , repoSize :: Int- , repoOwner :: User- , repoPrivate :: Bool- , repoOpenIssues :: Int+ , repoStargazersCount :: Int+ , repoWatchersCount :: Int+ , repoLanguage :: Text , repoHasIssues :: Bool , repoHasDownloads :: Bool , repoHasWiki :: Bool- , repoLanguage :: Text- , repoCreatedAt :: Int- , repoPushedAt :: Int- , repoMasterBranch :: Text+ , repoHasPages :: Bool+ , repoForksCount :: Int+ , repoMirrorUrl :: Maybe Url+ , repoOpenIssuesCount :: Int+ -- for compatiblity?+ , repoForks :: Int+ , repoOpenIssues :: Int+ , repoWatchers :: Int+ , repoStargazers :: Maybe Int+ , repoMasterBranch :: Maybe Text } deriving (Show, Eq, Typeable) instance FromJSON Repository where parseJSON (Object o) = Repository <$> o .: "id" <*> o .: "name"- <*> o .: "url"+ <*> o .: "full_name"+ <*> o .: "owner"+ <*> o .: "private"+ <*> o .: "html_url" <*> o .: "description"- <*> o .:? "homepage"- <*> o .: "watchers"- <*> o .: "stargazers"- <*> o .: "forks" <*> o .: "fork"+ <*> o .: "url"+ <*> o .: "forks_url"+ <*> o .: "keys_url"+ <*> o .: "collaborators_url"+ <*> o .: "teams_url"+ <*> o .: "hooks_url"+ <*> o .: "issue_events_url"+ <*> o .: "events_url"+ <*> o .: "assignees_url"+ <*> o .: "branches_url"+ <*> o .: "tags_url"+ <*> o .: "blobs_url"+ <*> o .: "git_tags_url"+ <*> o .: "git_refs_url"+ <*> o .: "trees_url"+ <*> o .: "statuses_url"+ <*> o .: "languages_url"+ <*> o .: "stargazers_url"+ <*> o .: "contributors_url"+ <*> o .: "subscribers_url"+ <*> o .: "subscription_url"+ <*> o .: "commits_url"+ <*> o .: "git_commits_url"+ <*> o .: "issue_comment_url"+ <*> o .: "contents_url"+ <*> o .: "compare_url"+ <*> o .: "merges_url"+ <*> o .: "archive_url"+ <*> o .: "downloads_url"+ <*> o .: "issues_url"+ <*> o .: "pulls_url"+ <*> o .: "milestones_url"+ <*> o .: "notifications_url"+ <*> o .: "labels_url"+ <*> o .: "releases_url"+ <*> o .: "created_at"+ <*> o .: "updated_at"+ <*> o .: "pushed_at"+ <*> o .: "git_url"+ <*> o .: "ssh_url"+ <*> o .: "clone_url"+ <*> o .: "svn_url"+ <*> o .:? "homepage" <*> o .: "size"- <*> o .: "owner"- <*> o .: "private"- <*> o .: "open_issues"+ <*> o .: "stargazers_count"+ <*> o .: "watchers_count"+ <*> o .: "language" <*> o .: "has_issues" <*> o .: "has_downloads" <*> o .: "has_wiki"- <*> o .: "language"- <*> o .: "created_at"- <*> o .: "pushed_at"- <*> o .: "master_branch"+ <*> o .: "has_pages"+ <*> o .: "forks_count"+ <*> o .:? "mirror_url"+ <*> o .: "open_issues_count"+ <*> o .: "forks"+ <*> o .: "open_issues"+ <*> o .: "watchers"+ <*> o .:? "stargazers"+ <*> o .:? "master_branch" parseJSON _ = fail "Repository must be an object" data User = User- { userName :: Text- , userEmail :: Maybe EmailAddress- , userUsername :: Maybe Text+ { userLogin :: Text+ , userId :: Int+ , userAvatarUrl :: Url+ , userGravatarId :: Text+ , userUrl :: Url+ , userHtmlUrl :: Url+ , userFollowersUrl :: Url+ , userFollowingUrl :: Url+ , userGistsUrl :: Url+ , userStarredUrl :: Url+ , userSubscriptionsUrl :: Url+ , userOrganizationsUrl :: Url+ , userReposUrl :: Url+ , userEventsUrl :: Url+ , userReceivedEventsUrl :: Url+ , userType :: Text+ , userSiteAdmin :: Bool } deriving (Show, Eq, Typeable) instance FromJSON User where parseJSON (Object o) = User+ <$> o .: "login"+ <*> o .: "id"+ <*> o .: "avatar_url"+ <*> o .: "gravatar_id"+ <*> o .: "url"+ <*> o .: "html_url"+ <*> o .: "followers_url"+ <*> o .: "following_url"+ <*> o .: "gists_url"+ <*> o .: "starred_url"+ <*> o .: "subscriptions_url"+ <*> o .: "organizations_url"+ <*> o .: "repos_url"+ <*> o .: "events_url"+ <*> o .: "received_events_url"+ <*> o .: "type"+ <*> o .: "site_admin"+ parseJSON _ = fail "User must be an object"++data SimpleUser = SimpleUser+ { simpleUserName :: Text+ , simpleUserEmail :: Maybe EmailAddress+ , simpleUserUsername :: Maybe Text+ , simpleUserDate :: Maybe Text+ } deriving (Show, Eq, Typeable)++instance FromJSON SimpleUser where+ parseJSON (Object o) = SimpleUser <$> o .: "name" <*> o .:? "email" <*> o .:? "username"- parseJSON _ = fail "User must be an object"+ <*> o .:? "date"+ parseJSON _ = fail "SimpleUser must be an object" instance FromJSON EmailAddress where parseJSON (String t) = case emailAddress $ B.pack . T.unpack $ t of Just a -> pure a Nothing -> fail "failed to parse EmailAddress" parseJSON _ = fail "EmailAddress must be a text"++data Branch = Branch+ { branchName :: Text+ , branchCommit :: SimpleCommit+ } deriving (Show, Eq, Typeable)++instance FromJSON Branch where+ parseJSON (Object o) = Branch+ <$> o .: "name"+ <*> o .: "commit"+ parseJSON _ = fail "Branch must be an object"++data SimpleCommit = SimpleCommit+ { simpleCommitSha :: Text+ , simpleCommitUrl :: Url+ , simpleCommitHtmlUrl :: Maybe Url+ } deriving (Show, Eq, Typeable)++instance FromJSON SimpleCommit where+ parseJSON (Object o) = SimpleCommit+ <$> o .: "sha"+ <*> o .: "url"+ <*> o .:? "html_url"+ parseJSON _ = fail "SimpleCommit must be an object"++-- | used in StatusEvent+data StatusCommit = StatusCommit+ { statusCommitSHA :: Text+ , statusCommitCommit :: SimpleStatusCommit+ , statusCommitUrl :: Url+ , statusCommitHtmlUrl :: Url+ , statusCommitCommentsUrl :: Url+ , statusCommitAuthor :: User+ , statusCommitCommitter :: User+ , statusCommitParents :: [SimpleCommit]+ } deriving (Show, Eq, Typeable)++instance FromJSON StatusCommit where+ parseJSON (Object o) = StatusCommit+ <$> o .: "sha"+ <*> o .: "commit"+ <*> o .: "url"+ <*> o .: "html_url"+ <*> o .: "comments_url"+ <*> o .: "author"+ <*> o .: "committer"+ <*> o .: "parents"+ parseJSON _ = fail "StatusCommit must be an object"++data SimpleStatusCommit = SimpleStatusCommit+ { simpleStatusCommitAuthor :: SimpleUser+ , simpleStatusCommitCommitter :: SimpleUser+ , simpleStatusCommitMessage :: Text+ , simpleStatusCommitTree :: Tree+ , simpleStatusCommitUrl :: Url+ , simpleStatusCommitCommentCount :: Int+ } deriving (Show, Eq, Typeable)++instance FromJSON SimpleStatusCommit where+ parseJSON (Object o) = SimpleStatusCommit+ <$> o .: "author"+ <*> o .: "committer"+ <*> o .: "message"+ <*> o .: "tree"+ <*> o .: "url"+ <*> o .: "comment_count"+ parseJSON _ = fail "SimpleStatusCommit must be an object"++data Tree = Tree+ { treeSHA :: Text+ , treeUrl :: Url+ } deriving (Show, Eq, Typeable)++instance FromJSON Tree where+ parseJSON (Object o) = Tree+ <$> o .: "sha"+ <*> o .: "url"+ parseJSON _ = fail "Tree must be an object"++type Url = Text++-- | Or a b represents a or b+-- The reason why we don't use Either type is that Either Int String type parses { "left": 1 } or { "right": "foo" }, but we want to parse 1 or "foo".+data Or a b = L a | R b deriving (Show, Eq, Typeable)++instance (FromJSON a, FromJSON b) => FromJSON (Or a b) where+ parseJSON v = L <$> parseJSON v <|> R <$> parseJSON v++toEither :: Or a b -> Either a b+toEither (L a) = Left a+toEither (R b) = Right b