packages feed

gerrit 0.1.0.0 → 0.1.1.0

raw patch · 13 files changed

+1909/−684 lines, 13 filesdep +aeson-casingdep +http-client-openssldep +timedep −http-client-tlsPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: aeson-casing, http-client-openssl, time

Dependencies removed: http-client-tls

API changes (from Hackage documentation)

- Gerrit: hasLabel :: Text -> Int -> GerritChange -> Bool
- Gerrit.Data: ABANDONED :: GerritChangeStatus
- Gerrit.Data: APPROVED :: GerritLabelVote
- Gerrit.Data: ChangeId :: Text -> GerritQuery
- Gerrit.Data: CommitMessage :: Text -> GerritQuery
- Gerrit.Data: DISLIKED :: GerritLabelVote
- Gerrit.Data: DRAFT :: GerritChangeStatus
- Gerrit.Data: GerritAccount :: Int -> GerritAccount
- Gerrit.Data: GerritChange :: Text -> Text -> Text -> Text -> GerritChangeStatus -> Maybe Bool -> Map Text (Maybe GerritRevision) -> Maybe Text -> Int -> Map Text GerritDetailedLabel -> GerritChange
- Gerrit.Data: GerritDetailedLabel :: [GerritDetailedLabelVote] -> Int -> GerritDetailedLabel
- Gerrit.Data: GerritDetailedLabelVote :: Maybe Int -> Int -> GerritDetailedLabelVote
- Gerrit.Data: GerritLabel :: Map GerritLabelVote GerritAccount -> GerritLabel
- Gerrit.Data: GerritRevision :: Text -> GerritRevisionKind -> GerritRevision
- Gerrit.Data: GerritVersion :: Text -> GerritVersion
- Gerrit.Data: MERGED :: GerritChangeStatus
- Gerrit.Data: NEW :: GerritChangeStatus
- Gerrit.Data: Owner :: Text -> GerritQuery
- Gerrit.Data: Project :: Text -> GerritQuery
- Gerrit.Data: RECOMMENDED :: GerritLabelVote
- Gerrit.Data: REJECTED :: GerritLabelVote
- Gerrit.Data: ReviewInput :: Maybe Text -> Maybe (Map Text Int) -> ReviewInput
- Gerrit.Data: ReviewResult :: Maybe (Map Text Int) -> Maybe Int -> ReviewResult
- Gerrit.Data: Status :: GerritChangeStatus -> GerritQuery
- Gerrit.Data: [account_id] :: GerritDetailedLabelVote -> Int
- Gerrit.Data: [all] :: GerritDetailedLabel -> [GerritDetailedLabelVote]
- Gerrit.Data: [branch] :: GerritChange -> Text
- Gerrit.Data: [current_revision] :: GerritChange -> Maybe Text
- Gerrit.Data: [default_value] :: GerritDetailedLabel -> Int
- Gerrit.Data: [id] :: GerritChange -> Text
- Gerrit.Data: [kind] :: GerritRevision -> GerritRevisionKind
- Gerrit.Data: [labels] :: GerritChange -> Map Text GerritDetailedLabel
- Gerrit.Data: [mergeable] :: GerritChange -> Maybe Bool
- Gerrit.Data: [number] :: GerritChange -> Int
- Gerrit.Data: [project] :: GerritChange -> Text
- Gerrit.Data: [ref] :: GerritRevision -> Text
- Gerrit.Data: [revisions] :: GerritChange -> Map Text (Maybe GerritRevision)
- Gerrit.Data: [riLabels] :: ReviewInput -> Maybe (Map Text Int)
- Gerrit.Data: [riMessage] :: ReviewInput -> Maybe Text
- Gerrit.Data: [rrLabels] :: ReviewResult -> Maybe (Map Text Int)
- Gerrit.Data: [rrReady] :: ReviewResult -> Maybe Int
- Gerrit.Data: [status] :: GerritChange -> GerritChangeStatus
- Gerrit.Data: [subject] :: GerritChange -> Text
- Gerrit.Data: [unused_account_id] :: GerritAccount -> Int
- Gerrit.Data: [value] :: GerritDetailedLabelVote -> Maybe Int
- Gerrit.Data: data GerritChange
- Gerrit.Data: data GerritChangeStatus
- Gerrit.Data: data GerritDetailedLabel
- Gerrit.Data: data GerritDetailedLabelVote
- Gerrit.Data: data GerritLabelVote
- Gerrit.Data: data GerritQuery
- Gerrit.Data: data GerritRevision
- Gerrit.Data: data ReviewInput
- Gerrit.Data: data ReviewResult
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritAccount
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritChange
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritChangeStatus
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritDetailedLabel
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritDetailedLabelVote
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritLabel
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritRevision
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritRevisionKind
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.GerritVersion
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.ReviewInput
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.ReviewResult
- Gerrit.Data: instance Data.Aeson.Types.FromJSON.FromJSONKey Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance Data.Aeson.Types.ToJSON.ToJSON Gerrit.Data.ReviewInput
- Gerrit.Data: instance Data.Aeson.Types.ToJSON.ToJSON Gerrit.Data.ReviewResult
- Gerrit.Data: instance GHC.Classes.Eq Gerrit.Data.GerritChangeStatus
- Gerrit.Data: instance GHC.Classes.Eq Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance GHC.Classes.Eq Gerrit.Data.GerritRevisionKind
- Gerrit.Data: instance GHC.Classes.Eq Gerrit.Data.ReviewInput
- Gerrit.Data: instance GHC.Classes.Eq Gerrit.Data.ReviewResult
- Gerrit.Data: instance GHC.Classes.Ord Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance GHC.Classes.Ord Gerrit.Data.ReviewInput
- Gerrit.Data: instance GHC.Classes.Ord Gerrit.Data.ReviewResult
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritAccount
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritChange
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritChangeStatus
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritDetailedLabel
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritDetailedLabelVote
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritLabel
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritRevision
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritRevisionKind
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.GerritVersion
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.ReviewInput
- Gerrit.Data: instance GHC.Generics.Generic Gerrit.Data.ReviewResult
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritAccount
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritChange
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritChangeStatus
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritDetailedLabel
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritDetailedLabelVote
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritLabel
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritLabelVote
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritRevision
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritRevisionKind
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.GerritVersion
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.ReviewInput
- Gerrit.Data: instance GHC.Show.Show Gerrit.Data.ReviewResult
- Gerrit.Data: newtype GerritAccount
- Gerrit.Data: newtype GerritLabel
- Gerrit.Data: newtype GerritVersion
- Gerrit.Data: queryText :: GerritQuery -> Text
- Gerrit.Event: AssigneeChanged :: Change -> User -> AssigneeChanged
- Gerrit.Event: AssigneeChangedEvent :: EventType
- Gerrit.Event: Change :: Text -> Text -> Text -> Text -> User -> Change
- Gerrit.Event: ChangeAbandoned :: Text -> User -> Change -> PatchSet -> ChangeAbandoned
- Gerrit.Event: ChangeAbandonedEvent :: EventType
- Gerrit.Event: ChangeDeleted :: Change -> User -> ChangeDeleted
- Gerrit.Event: ChangeDeletedEvent :: EventType
- Gerrit.Event: ChangeMerged :: Text -> User -> Change -> PatchSet -> ChangeMerged
- Gerrit.Event: ChangeMergedEvent :: EventType
- Gerrit.Event: ChangeRestored :: Change -> PatchSet -> User -> Text -> ChangeRestored
- Gerrit.Event: ChangeRestoredEvent :: EventType
- Gerrit.Event: CommentAdded :: Change -> PatchSet -> User -> CommentAdded
- Gerrit.Event: CommentAddedEvent :: EventType
- Gerrit.Event: DroppedOutputEvent :: EventType
- Gerrit.Event: EventAssigneeChanged :: AssigneeChanged -> Event
- Gerrit.Event: EventChangeAbandoned :: ChangeAbandoned -> Event
- Gerrit.Event: EventChangeDeleted :: ChangeDeleted -> Event
- Gerrit.Event: EventChangeMerged :: ChangeMerged -> Event
- Gerrit.Event: EventChangeRestored :: ChangeRestored -> Event
- Gerrit.Event: EventCommentAdded :: CommentAdded -> Event
- Gerrit.Event: EventDroppedOutput :: Event
- Gerrit.Event: EventHashtagsChanged :: HashtagsChanged -> Event
- Gerrit.Event: EventPatchsetCreated :: PatchsetCreated -> Event
- Gerrit.Event: EventPrivateStateChanged :: PrivateStateChanged -> Event
- Gerrit.Event: EventProjectCreated :: ProjectCreated -> Event
- Gerrit.Event: EventRefUpdated :: RefUpdated -> Event
- Gerrit.Event: EventReviewerAdded :: ReviewerAdded -> Event
- Gerrit.Event: EventReviewerDeleted :: ReviewerDeleted -> Event
- Gerrit.Event: EventTopicChanged :: TopicChanged -> Event
- Gerrit.Event: EventVoteDeleted :: VoteDeleted -> Event
- Gerrit.Event: EventWorkInProgressStateChanged :: WorkInProgressStateChanged -> Event
- Gerrit.Event: HashtagsChanged :: Change -> User -> HashtagsChanged
- Gerrit.Event: HashtagsChangedEvent :: EventType
- Gerrit.Event: PatchSet :: Int -> Text -> User -> User -> PatchSet
- Gerrit.Event: PatchsetCreated :: User -> Change -> PatchSet -> PatchsetCreated
- Gerrit.Event: PatchsetCreatedEvent :: EventType
- Gerrit.Event: PrivateStateChanged :: Change -> PatchSet -> User -> PrivateStateChanged
- Gerrit.Event: PrivateStateChangedEvent :: EventType
- Gerrit.Event: ProjectCreated :: Text -> Text -> ProjectCreated
- Gerrit.Event: ProjectCreatedEvent :: EventType
- Gerrit.Event: Ref :: Text -> Text -> Text -> Text -> Ref
- Gerrit.Event: RefUpdated :: User -> Ref -> RefUpdated
- Gerrit.Event: RefUpdatedEvent :: EventType
- Gerrit.Event: ReviewerAdded :: Change -> PatchSet -> User -> ReviewerAdded
- Gerrit.Event: ReviewerAddedEvent :: EventType
- Gerrit.Event: ReviewerDeleted :: Change -> PatchSet -> User -> ReviewerDeleted
- Gerrit.Event: ReviewerDeletedEvent :: EventType
- Gerrit.Event: TopicChanged :: Change -> User -> Text -> TopicChanged
- Gerrit.Event: TopicChangedEvent :: EventType
- Gerrit.Event: User :: Maybe Text -> Maybe Text -> Maybe Text -> User
- Gerrit.Event: VoteDeleted :: Change -> PatchSet -> User -> VoteDeleted
- Gerrit.Event: VoteDeletedEvent :: EventType
- Gerrit.Event: WorkInProgressStateChanged :: Change -> PatchSet -> User -> WorkInProgressStateChanged
- Gerrit.Event: WorkInProgressStateChangedEvent :: EventType
- Gerrit.Event: [assigneeChangedChange] :: AssigneeChanged -> Change
- Gerrit.Event: [assigneeChangedChanger] :: AssigneeChanged -> User
- Gerrit.Event: [changeAbandonedAbandoner] :: ChangeAbandoned -> User
- Gerrit.Event: [changeAbandonedChange] :: ChangeAbandoned -> Change
- Gerrit.Event: [changeAbandonedPatchSet] :: ChangeAbandoned -> PatchSet
- Gerrit.Event: [changeAbandonedProject] :: ChangeAbandoned -> Text
- Gerrit.Event: [changeBranch] :: Change -> Text
- Gerrit.Event: [changeDeletedChange] :: ChangeDeleted -> Change
- Gerrit.Event: [changeDeletedDeleter] :: ChangeDeleted -> User
- Gerrit.Event: [changeMergedChange] :: ChangeMerged -> Change
- Gerrit.Event: [changeMergedPatchSet] :: ChangeMerged -> PatchSet
- Gerrit.Event: [changeMergedProject] :: ChangeMerged -> Text
- Gerrit.Event: [changeMergedSubmitter] :: ChangeMerged -> User
- Gerrit.Event: [changeOwner] :: Change -> User
- Gerrit.Event: [changeProject] :: Change -> Text
- Gerrit.Event: [changeRestoredChange] :: ChangeRestored -> Change
- Gerrit.Event: [changeRestoredPatchSet] :: ChangeRestored -> PatchSet
- Gerrit.Event: [changeRestoredReason] :: ChangeRestored -> Text
- Gerrit.Event: [changeRestoredRestorer] :: ChangeRestored -> User
- Gerrit.Event: [changeSubject] :: Change -> Text
- Gerrit.Event: [changeUrl] :: Change -> Text
- Gerrit.Event: [commentAddedAuthor] :: CommentAdded -> User
- Gerrit.Event: [commentAddedChange] :: CommentAdded -> Change
- Gerrit.Event: [commentAddedPatchSet] :: CommentAdded -> PatchSet
- Gerrit.Event: [hashtagsChangedChange] :: HashtagsChanged -> Change
- Gerrit.Event: [hashtagsChangedEditor] :: HashtagsChanged -> User
- Gerrit.Event: [patchSetNumber] :: PatchSet -> Int
- Gerrit.Event: [patchSetRef] :: PatchSet -> Text
- Gerrit.Event: [patchSetUploader] :: PatchSet -> User
- Gerrit.Event: [patchsetAuthor] :: PatchSet -> User
- Gerrit.Event: [patchsetCreatedChange] :: PatchsetCreated -> Change
- Gerrit.Event: [patchsetCreatedPatchSet] :: PatchsetCreated -> PatchSet
- Gerrit.Event: [patchsetCreatedUploader] :: PatchsetCreated -> User
- Gerrit.Event: [privateStateChangedChange] :: PrivateStateChanged -> Change
- Gerrit.Event: [privateStateChangedChanger] :: PrivateStateChanged -> User
- Gerrit.Event: [privateStateChangedPatchSet] :: PrivateStateChanged -> PatchSet
- Gerrit.Event: [projectCreatedHeadName] :: ProjectCreated -> Text
- Gerrit.Event: [projectCreatedProjectName] :: ProjectCreated -> Text
- Gerrit.Event: [refNewRev] :: Ref -> Text
- Gerrit.Event: [refOldRev] :: Ref -> Text
- Gerrit.Event: [refProject] :: Ref -> Text
- Gerrit.Event: [refRefName] :: Ref -> Text
- Gerrit.Event: [refUpdatedRefUpdate] :: RefUpdated -> Ref
- Gerrit.Event: [refUpdatedSubmitter] :: RefUpdated -> User
- Gerrit.Event: [reviewerAddedAdder] :: ReviewerAdded -> User
- Gerrit.Event: [reviewerAddedChange] :: ReviewerAdded -> Change
- Gerrit.Event: [reviewerAddedPatchSet] :: ReviewerAdded -> PatchSet
- Gerrit.Event: [reviewerDeletedChange] :: ReviewerDeleted -> Change
- Gerrit.Event: [reviewerDeletedPatchSet] :: ReviewerDeleted -> PatchSet
- Gerrit.Event: [reviewerDeletedRemover] :: ReviewerDeleted -> User
- Gerrit.Event: [topicChangedChange] :: TopicChanged -> Change
- Gerrit.Event: [topicChangedChanger] :: TopicChanged -> User
- Gerrit.Event: [topicChangedOldTopic] :: TopicChanged -> Text
- Gerrit.Event: [userEmail] :: User -> Maybe Text
- Gerrit.Event: [userName] :: User -> Maybe Text
- Gerrit.Event: [userUsername] :: User -> Maybe Text
- Gerrit.Event: [voteDeletedChange] :: VoteDeleted -> Change
- Gerrit.Event: [voteDeletedPatchSet] :: VoteDeleted -> PatchSet
- Gerrit.Event: [voteDeletedRemover] :: VoteDeleted -> User
- Gerrit.Event: [workInProgressStateChangedChange] :: WorkInProgressStateChanged -> Change
- Gerrit.Event: [workInProgressStateChangedChanger] :: WorkInProgressStateChanged -> User
- Gerrit.Event: [workInProgressStateChangedPatchSet] :: WorkInProgressStateChanged -> PatchSet
- Gerrit.Event: data AssigneeChanged
- Gerrit.Event: data Change
- Gerrit.Event: data ChangeAbandoned
- Gerrit.Event: data ChangeDeleted
- Gerrit.Event: data ChangeMerged
- Gerrit.Event: data ChangeRestored
- Gerrit.Event: data CommentAdded
- Gerrit.Event: data Event
- Gerrit.Event: data EventType
- Gerrit.Event: data HashtagsChanged
- Gerrit.Event: data PatchSet
- Gerrit.Event: data PatchsetCreated
- Gerrit.Event: data PrivateStateChanged
- Gerrit.Event: data ProjectCreated
- Gerrit.Event: data Ref
- Gerrit.Event: data RefUpdated
- Gerrit.Event: data ReviewerAdded
- Gerrit.Event: data ReviewerDeleted
- Gerrit.Event: data TopicChanged
- Gerrit.Event: data User
- Gerrit.Event: data VoteDeleted
- Gerrit.Event: data WorkInProgressStateChanged
- Gerrit.Event: eventName :: EventType -> Text
- Gerrit.Event: getChange :: Event -> Maybe Change
- Gerrit.Event: getEventType :: Event -> EventType
- Gerrit.Event: getUser :: Event -> Maybe User
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.AssigneeChanged
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.Change
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ChangeAbandoned
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ChangeDeleted
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ChangeMerged
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ChangeRestored
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.CommentAdded
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.Event
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.EventType
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.HashtagsChanged
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.PatchSet
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.PatchsetCreated
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.PrivateStateChanged
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ProjectCreated
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.Ref
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.RefUpdated
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ReviewerAdded
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.ReviewerDeleted
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.TopicChanged
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.User
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.VoteDeleted
- Gerrit.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Event.WorkInProgressStateChanged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.AssigneeChanged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.Change
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ChangeAbandoned
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ChangeDeleted
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ChangeMerged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ChangeRestored
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.CommentAdded
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.Event
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.EventType
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.HashtagsChanged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.PatchSet
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.PatchsetCreated
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.PrivateStateChanged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ProjectCreated
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.Ref
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.RefUpdated
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ReviewerAdded
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.ReviewerDeleted
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.TopicChanged
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.User
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.VoteDeleted
- Gerrit.Event: instance GHC.Classes.Eq Gerrit.Event.WorkInProgressStateChanged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.AssigneeChanged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.Change
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ChangeAbandoned
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ChangeDeleted
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ChangeMerged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ChangeRestored
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.CommentAdded
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.HashtagsChanged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.PatchSet
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.PatchsetCreated
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.PrivateStateChanged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ProjectCreated
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.Ref
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.RefUpdated
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ReviewerAdded
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.ReviewerDeleted
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.TopicChanged
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.User
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.VoteDeleted
- Gerrit.Event: instance GHC.Generics.Generic Gerrit.Event.WorkInProgressStateChanged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.AssigneeChanged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.Change
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ChangeAbandoned
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ChangeDeleted
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ChangeMerged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ChangeRestored
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.CommentAdded
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.Event
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.EventType
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.HashtagsChanged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.PatchSet
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.PatchsetCreated
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.PrivateStateChanged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ProjectCreated
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.Ref
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.RefUpdated
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ReviewerAdded
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.ReviewerDeleted
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.TopicChanged
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.User
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.VoteDeleted
- Gerrit.Event: instance GHC.Show.Show Gerrit.Event.WorkInProgressStateChanged
+ Gerrit: After :: UTCTime -> GerritQuery
+ Gerrit: CanSee :: Text -> GerritAccountQuery
+ Gerrit: Email :: Text -> GerritAccountQuery
+ Gerrit: GerritAccount :: Int -> Text -> Maybe Text -> Maybe Text -> Maybe Bool -> GerritAccount
+ Gerrit: IsActive :: GerritAccountQuery
+ Gerrit: IsInactive :: GerritAccountQuery
+ Gerrit: Name :: Text -> GerritAccountQuery
+ Gerrit: Prefix :: Text -> GerritProjectQuery
+ Gerrit: Regexp :: Text -> GerritProjectQuery
+ Gerrit: Username :: Text -> GerritAccountQuery
+ Gerrit: [created] :: GerritChange -> GerritTime
+ Gerrit: [deletions] :: GerritChange -> Int
+ Gerrit: [gerritAccountEmail] :: GerritAccount -> Maybe Text
+ Gerrit: [gerritAccountHasMore] :: GerritAccount -> Maybe Bool
+ Gerrit: [gerritAccountId] :: GerritAccount -> Int
+ Gerrit: [gerritAccountName] :: GerritAccount -> Text
+ Gerrit: [gerritAccountUsername] :: GerritAccount -> Maybe Text
+ Gerrit: [insertions] :: GerritChange -> Int
+ Gerrit: [messages] :: GerritChange -> [GerritChangeMessage]
+ Gerrit: [more_changes] :: GerritChange -> Maybe Bool
+ Gerrit: [owner] :: GerritChange -> GerritAuthor
+ Gerrit: [submitted] :: GerritChange -> Maybe GerritTime
+ Gerrit: [submitter] :: GerritChange -> Maybe GerritAuthor
+ Gerrit: [updated] :: GerritChange -> GerritTime
+ Gerrit: data GerritAccount
+ Gerrit: data GerritAccountQuery
+ Gerrit: data GerritProjectQuery
+ Gerrit: getAccount :: Int -> NonEmpty GerritAccountQuery -> GerritClient -> IO [GerritAccount]
+ Gerrit: getAccountId :: Int -> NonEmpty GerritAccountQuery -> GerritClient -> IO [GerritAccountId]
+ Gerrit: getClient :: Text -> Maybe (Text, Text) -> IO GerritClient
+ Gerrit: getProjects :: Int -> GerritProjectQuery -> Maybe Int -> GerritClient -> IO GerritProjectsMessage
+ Gerrit: serverUrl :: GerritClient -> Text
+ Gerrit.Data.Account: CanSee :: Text -> GerritAccountQuery
+ Gerrit.Data.Account: Email :: Text -> GerritAccountQuery
+ Gerrit.Data.Account: GerritAccount :: Int -> Text -> Maybe Text -> Maybe Text -> Maybe Bool -> GerritAccount
+ Gerrit.Data.Account: GerritAccountId :: Int -> Maybe Bool -> GerritAccountId
+ Gerrit.Data.Account: IsActive :: GerritAccountQuery
+ Gerrit.Data.Account: IsInactive :: GerritAccountQuery
+ Gerrit.Data.Account: Name :: Text -> GerritAccountQuery
+ Gerrit.Data.Account: Username :: Text -> GerritAccountQuery
+ Gerrit.Data.Account: [gerritAccountEmail] :: GerritAccount -> Maybe Text
+ Gerrit.Data.Account: [gerritAccountHasMore'] :: GerritAccountId -> Maybe Bool
+ Gerrit.Data.Account: [gerritAccountHasMore] :: GerritAccount -> Maybe Bool
+ Gerrit.Data.Account: [gerritAccountId'] :: GerritAccountId -> Int
+ Gerrit.Data.Account: [gerritAccountId] :: GerritAccount -> Int
+ Gerrit.Data.Account: [gerritAccountName] :: GerritAccount -> Text
+ Gerrit.Data.Account: [gerritAccountUsername] :: GerritAccount -> Maybe Text
+ Gerrit.Data.Account: accountQs :: Int -> NonEmpty GerritAccountQuery -> Text
+ Gerrit.Data.Account: data GerritAccount
+ Gerrit.Data.Account: data GerritAccountId
+ Gerrit.Data.Account: data GerritAccountQuery
+ Gerrit.Data.Account: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Account.GerritAccount
+ Gerrit.Data.Account: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Account.GerritAccountId
+ Gerrit.Data.Account: instance GHC.Classes.Eq Gerrit.Data.Account.GerritAccount
+ Gerrit.Data.Account: instance GHC.Classes.Eq Gerrit.Data.Account.GerritAccountId
+ Gerrit.Data.Account: instance GHC.Show.Show Gerrit.Data.Account.GerritAccount
+ Gerrit.Data.Account: instance GHC.Show.Show Gerrit.Data.Account.GerritAccountId
+ Gerrit.Data.Account: userQueryText :: GerritAccountQuery -> Text
+ Gerrit.Data.Change: ABANDONED :: GerritChangeStatus
+ Gerrit.Data.Change: After :: UTCTime -> GerritQuery
+ Gerrit.Data.Change: ChangeId :: Text -> GerritQuery
+ Gerrit.Data.Change: CommitMessage :: Text -> GerritQuery
+ Gerrit.Data.Change: DRAFT :: GerritChangeStatus
+ Gerrit.Data.Change: GerritAuthor :: Int -> Text -> Maybe Text -> Maybe Text -> GerritAuthor
+ Gerrit.Data.Change: GerritChange :: Text -> Text -> Text -> Text -> GerritChangeStatus -> Maybe Bool -> Map Text (Maybe GerritRevision) -> Maybe Text -> Int -> Map Text GerritDetailedLabel -> [GerritChangeMessage] -> GerritAuthor -> GerritTime -> GerritTime -> Maybe GerritTime -> Maybe GerritAuthor -> Int -> Int -> Maybe Bool -> GerritChange
+ Gerrit.Data.Change: GerritChangeMessage :: Text -> Maybe GerritAuthor -> GerritTime -> Text -> GerritChangeMessage
+ Gerrit.Data.Change: GerritCommit :: GerritCommitAuthor -> GerritCommitAuthor -> Text -> Text -> GerritCommit
+ Gerrit.Data.Change: GerritCommitAuthor :: Text -> Maybe Text -> GerritTime -> GerritCommitAuthor
+ Gerrit.Data.Change: GerritDetailedLabel :: Maybe [GerritDetailedLabelVote] -> Int -> GerritDetailedLabel
+ Gerrit.Data.Change: GerritDetailedLabelVote :: Maybe Int -> Int -> GerritDetailedLabelVote
+ Gerrit.Data.Change: GerritFile :: Maybe Text -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> GerritFile
+ Gerrit.Data.Change: GerritRevision :: Text -> GerritRevisionKind -> Map Text GerritFile -> GerritCommit -> GerritAuthor -> GerritRevision
+ Gerrit.Data.Change: GerritTime :: UTCTime -> GerritTime
+ Gerrit.Data.Change: MERGED :: GerritChangeStatus
+ Gerrit.Data.Change: MERGE_FIRST_PARENT_UPDATE :: GerritRevisionKind
+ Gerrit.Data.Change: NEW :: GerritChangeStatus
+ Gerrit.Data.Change: NO_CHANGE :: GerritRevisionKind
+ Gerrit.Data.Change: NO_CODE_CHANGE :: GerritRevisionKind
+ Gerrit.Data.Change: Owner :: Text -> GerritQuery
+ Gerrit.Data.Change: Project :: Text -> GerritQuery
+ Gerrit.Data.Change: REWORK :: GerritRevisionKind
+ Gerrit.Data.Change: Status :: GerritChangeStatus -> GerritQuery
+ Gerrit.Data.Change: TRIVIAL_REBASE :: GerritRevisionKind
+ Gerrit.Data.Change: [aAccountId] :: GerritAuthor -> Int
+ Gerrit.Data.Change: [aEmail] :: GerritAuthor -> Maybe Text
+ Gerrit.Data.Change: [aName] :: GerritAuthor -> Text
+ Gerrit.Data.Change: [aUsername] :: GerritAuthor -> Maybe Text
+ Gerrit.Data.Change: [account_id] :: GerritDetailedLabelVote -> Int
+ Gerrit.Data.Change: [all] :: GerritDetailedLabel -> Maybe [GerritDetailedLabelVote]
+ Gerrit.Data.Change: [branch] :: GerritChange -> Text
+ Gerrit.Data.Change: [cAuthor] :: GerritCommit -> GerritCommitAuthor
+ Gerrit.Data.Change: [cCommitter] :: GerritCommit -> GerritCommitAuthor
+ Gerrit.Data.Change: [cMessage] :: GerritCommit -> Text
+ Gerrit.Data.Change: [cSubject] :: GerritCommit -> Text
+ Gerrit.Data.Change: [caDate] :: GerritCommitAuthor -> GerritTime
+ Gerrit.Data.Change: [caEmail] :: GerritCommitAuthor -> Maybe Text
+ Gerrit.Data.Change: [caName] :: GerritCommitAuthor -> Text
+ Gerrit.Data.Change: [created] :: GerritChange -> GerritTime
+ Gerrit.Data.Change: [current_revision] :: GerritChange -> Maybe Text
+ Gerrit.Data.Change: [default_value] :: GerritDetailedLabel -> Int
+ Gerrit.Data.Change: [deletions] :: GerritChange -> Int
+ Gerrit.Data.Change: [gfLinesDeleted] :: GerritFile -> Maybe Int
+ Gerrit.Data.Change: [gfLinesInserted] :: GerritFile -> Maybe Int
+ Gerrit.Data.Change: [gfSizeDelta] :: GerritFile -> Maybe Int
+ Gerrit.Data.Change: [gfSize] :: GerritFile -> Maybe Int
+ Gerrit.Data.Change: [gfStatus] :: GerritFile -> Maybe Text
+ Gerrit.Data.Change: [grCommit] :: GerritRevision -> GerritCommit
+ Gerrit.Data.Change: [grFiles] :: GerritRevision -> Map Text GerritFile
+ Gerrit.Data.Change: [grKind] :: GerritRevision -> GerritRevisionKind
+ Gerrit.Data.Change: [grRef] :: GerritRevision -> Text
+ Gerrit.Data.Change: [grUploader] :: GerritRevision -> GerritAuthor
+ Gerrit.Data.Change: [id] :: GerritChange -> Text
+ Gerrit.Data.Change: [insertions] :: GerritChange -> Int
+ Gerrit.Data.Change: [labels] :: GerritChange -> Map Text GerritDetailedLabel
+ Gerrit.Data.Change: [mAuthor] :: GerritChangeMessage -> Maybe GerritAuthor
+ Gerrit.Data.Change: [mDate] :: GerritChangeMessage -> GerritTime
+ Gerrit.Data.Change: [mId] :: GerritChangeMessage -> Text
+ Gerrit.Data.Change: [mMessage] :: GerritChangeMessage -> Text
+ Gerrit.Data.Change: [mergeable] :: GerritChange -> Maybe Bool
+ Gerrit.Data.Change: [messages] :: GerritChange -> [GerritChangeMessage]
+ Gerrit.Data.Change: [more_changes] :: GerritChange -> Maybe Bool
+ Gerrit.Data.Change: [number] :: GerritChange -> Int
+ Gerrit.Data.Change: [owner] :: GerritChange -> GerritAuthor
+ Gerrit.Data.Change: [project] :: GerritChange -> Text
+ Gerrit.Data.Change: [revisions] :: GerritChange -> Map Text (Maybe GerritRevision)
+ Gerrit.Data.Change: [status] :: GerritChange -> GerritChangeStatus
+ Gerrit.Data.Change: [subject] :: GerritChange -> Text
+ Gerrit.Data.Change: [submitted] :: GerritChange -> Maybe GerritTime
+ Gerrit.Data.Change: [submitter] :: GerritChange -> Maybe GerritAuthor
+ Gerrit.Data.Change: [unGerritTime] :: GerritTime -> UTCTime
+ Gerrit.Data.Change: [updated] :: GerritChange -> GerritTime
+ Gerrit.Data.Change: [value] :: GerritDetailedLabelVote -> Maybe Int
+ Gerrit.Data.Change: changeQS :: Int -> [GerritQuery] -> Maybe Int -> Text
+ Gerrit.Data.Change: data GerritAuthor
+ Gerrit.Data.Change: data GerritChange
+ Gerrit.Data.Change: data GerritChangeMessage
+ Gerrit.Data.Change: data GerritChangeStatus
+ Gerrit.Data.Change: data GerritCommit
+ Gerrit.Data.Change: data GerritCommitAuthor
+ Gerrit.Data.Change: data GerritDetailedLabel
+ Gerrit.Data.Change: data GerritDetailedLabelVote
+ Gerrit.Data.Change: data GerritFile
+ Gerrit.Data.Change: data GerritQuery
+ Gerrit.Data.Change: data GerritRevision
+ Gerrit.Data.Change: data GerritRevisionKind
+ Gerrit.Data.Change: defaultQueryChangeOptions :: Text
+ Gerrit.Data.Change: hasLabel :: Text -> Int -> GerritChange -> Bool
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritAuthor
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritChange
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritChangeMessage
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritChangeStatus
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritCommit
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritCommitAuthor
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritDetailedLabel
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritDetailedLabelVote
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritFile
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritRevision
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritRevisionKind
+ Gerrit.Data.Change: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Change.GerritTime
+ Gerrit.Data.Change: instance GHC.Classes.Eq Gerrit.Data.Change.GerritChangeStatus
+ Gerrit.Data.Change: instance GHC.Classes.Eq Gerrit.Data.Change.GerritRevisionKind
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritAuthor
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritChange
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritChangeMessage
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritChangeStatus
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritCommit
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritCommitAuthor
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritDetailedLabel
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritDetailedLabelVote
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritFile
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritRevision
+ Gerrit.Data.Change: instance GHC.Generics.Generic Gerrit.Data.Change.GerritRevisionKind
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritAuthor
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritChange
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritChangeMessage
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritChangeStatus
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritCommit
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritCommitAuthor
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritDetailedLabel
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritDetailedLabelVote
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritFile
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritRevision
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritRevisionKind
+ Gerrit.Data.Change: instance GHC.Show.Show Gerrit.Data.Change.GerritTime
+ Gerrit.Data.Change: newtype GerritTime
+ Gerrit.Data.Change: queryText :: GerritQuery -> Text
+ Gerrit.Data.Event: AssigneeChanged :: Change -> User -> Int64 -> AssigneeChanged
+ Gerrit.Data.Event: AssigneeChangedEvent :: EventType
+ Gerrit.Data.Event: Change :: Text -> Text -> Text -> Int -> Text -> User -> Change
+ Gerrit.Data.Event: ChangeAbandoned :: Text -> User -> Change -> PatchSet -> Maybe Text -> Int64 -> ChangeAbandoned
+ Gerrit.Data.Event: ChangeAbandonedEvent :: EventType
+ Gerrit.Data.Event: ChangeDeleted :: Change -> User -> ChangeDeleted
+ Gerrit.Data.Event: ChangeDeletedEvent :: EventType
+ Gerrit.Data.Event: ChangeMerged :: Text -> User -> Change -> PatchSet -> Int64 -> ChangeMerged
+ Gerrit.Data.Event: ChangeMergedEvent :: EventType
+ Gerrit.Data.Event: ChangeRestored :: Change -> PatchSet -> User -> Maybe Text -> Int64 -> ChangeRestored
+ Gerrit.Data.Event: ChangeRestoredEvent :: EventType
+ Gerrit.Data.Event: CommentAdded :: Change -> PatchSet -> User -> Int64 -> CommentAdded
+ Gerrit.Data.Event: CommentAddedEvent :: EventType
+ Gerrit.Data.Event: DroppedOutputEvent :: EventType
+ Gerrit.Data.Event: EventAssigneeChanged :: AssigneeChanged -> Event
+ Gerrit.Data.Event: EventChangeAbandoned :: ChangeAbandoned -> Event
+ Gerrit.Data.Event: EventChangeDeleted :: ChangeDeleted -> Event
+ Gerrit.Data.Event: EventChangeMerged :: ChangeMerged -> Event
+ Gerrit.Data.Event: EventChangeRestored :: ChangeRestored -> Event
+ Gerrit.Data.Event: EventCommentAdded :: CommentAdded -> Event
+ Gerrit.Data.Event: EventDroppedOutput :: Event
+ Gerrit.Data.Event: EventHashtagsChanged :: HashtagsChanged -> Event
+ Gerrit.Data.Event: EventPatchsetCreated :: PatchsetCreated -> Event
+ Gerrit.Data.Event: EventPrivateStateChanged :: PrivateStateChanged -> Event
+ Gerrit.Data.Event: EventProjectCreated :: ProjectCreated -> Event
+ Gerrit.Data.Event: EventRefUpdated :: RefUpdated -> Event
+ Gerrit.Data.Event: EventReviewerAdded :: ReviewerAdded -> Event
+ Gerrit.Data.Event: EventReviewerDeleted :: ReviewerDeleted -> Event
+ Gerrit.Data.Event: EventTopicChanged :: TopicChanged -> Event
+ Gerrit.Data.Event: EventVoteDeleted :: VoteDeleted -> Event
+ Gerrit.Data.Event: EventWorkInProgressStateChanged :: WorkInProgressStateChanged -> Event
+ Gerrit.Data.Event: HashtagsChanged :: Change -> User -> Int64 -> HashtagsChanged
+ Gerrit.Data.Event: HashtagsChangedEvent :: EventType
+ Gerrit.Data.Event: PatchSet :: Int -> Text -> User -> User -> PatchSet
+ Gerrit.Data.Event: PatchsetCreated :: User -> Change -> PatchSet -> Int64 -> PatchsetCreated
+ Gerrit.Data.Event: PatchsetCreatedEvent :: EventType
+ Gerrit.Data.Event: PrivateStateChanged :: Change -> PatchSet -> User -> Int64 -> PrivateStateChanged
+ Gerrit.Data.Event: PrivateStateChangedEvent :: EventType
+ Gerrit.Data.Event: ProjectCreated :: Text -> Text -> Int64 -> ProjectCreated
+ Gerrit.Data.Event: ProjectCreatedEvent :: EventType
+ Gerrit.Data.Event: Ref :: Text -> Text -> Text -> Text -> Ref
+ Gerrit.Data.Event: RefUpdated :: User -> Ref -> Int64 -> RefUpdated
+ Gerrit.Data.Event: RefUpdatedEvent :: EventType
+ Gerrit.Data.Event: ReviewerAdded :: Change -> PatchSet -> User -> Int64 -> ReviewerAdded
+ Gerrit.Data.Event: ReviewerAddedEvent :: EventType
+ Gerrit.Data.Event: ReviewerDeleted :: Change -> PatchSet -> User -> Int64 -> ReviewerDeleted
+ Gerrit.Data.Event: ReviewerDeletedEvent :: EventType
+ Gerrit.Data.Event: TopicChanged :: Change -> User -> Text -> Int64 -> TopicChanged
+ Gerrit.Data.Event: TopicChangedEvent :: EventType
+ Gerrit.Data.Event: User :: Maybe Text -> Maybe Text -> Maybe Text -> User
+ Gerrit.Data.Event: VoteDeleted :: Change -> PatchSet -> User -> VoteDeleted
+ Gerrit.Data.Event: VoteDeletedEvent :: EventType
+ Gerrit.Data.Event: WorkInProgressStateChanged :: Change -> PatchSet -> User -> Int64 -> WorkInProgressStateChanged
+ Gerrit.Data.Event: WorkInProgressStateChangedEvent :: EventType
+ Gerrit.Data.Event: [assigneeChangedChange] :: AssigneeChanged -> Change
+ Gerrit.Data.Event: [assigneeChangedChanger] :: AssigneeChanged -> User
+ Gerrit.Data.Event: [assigneeChangedEventCreatedOn] :: AssigneeChanged -> Int64
+ Gerrit.Data.Event: [changeAbandonedAbandoner] :: ChangeAbandoned -> User
+ Gerrit.Data.Event: [changeAbandonedChange] :: ChangeAbandoned -> Change
+ Gerrit.Data.Event: [changeAbandonedEventCreatedOn] :: ChangeAbandoned -> Int64
+ Gerrit.Data.Event: [changeAbandonedPatchSet] :: ChangeAbandoned -> PatchSet
+ Gerrit.Data.Event: [changeAbandonedProject] :: ChangeAbandoned -> Text
+ Gerrit.Data.Event: [changeAbandonedReason] :: ChangeAbandoned -> Maybe Text
+ Gerrit.Data.Event: [changeBranch] :: Change -> Text
+ Gerrit.Data.Event: [changeDeletedChange] :: ChangeDeleted -> Change
+ Gerrit.Data.Event: [changeDeletedDeleter] :: ChangeDeleted -> User
+ Gerrit.Data.Event: [changeMergedChange] :: ChangeMerged -> Change
+ Gerrit.Data.Event: [changeMergedEventCreatedOn] :: ChangeMerged -> Int64
+ Gerrit.Data.Event: [changeMergedPatchSet] :: ChangeMerged -> PatchSet
+ Gerrit.Data.Event: [changeMergedProject] :: ChangeMerged -> Text
+ Gerrit.Data.Event: [changeMergedSubmitter] :: ChangeMerged -> User
+ Gerrit.Data.Event: [changeNumber] :: Change -> Int
+ Gerrit.Data.Event: [changeOwner] :: Change -> User
+ Gerrit.Data.Event: [changeProject] :: Change -> Text
+ Gerrit.Data.Event: [changeRestoredChange] :: ChangeRestored -> Change
+ Gerrit.Data.Event: [changeRestoredEventCreatedOn] :: ChangeRestored -> Int64
+ Gerrit.Data.Event: [changeRestoredPatchSet] :: ChangeRestored -> PatchSet
+ Gerrit.Data.Event: [changeRestoredReason] :: ChangeRestored -> Maybe Text
+ Gerrit.Data.Event: [changeRestoredRestorer] :: ChangeRestored -> User
+ Gerrit.Data.Event: [changeSubject] :: Change -> Text
+ Gerrit.Data.Event: [changeUrl] :: Change -> Text
+ Gerrit.Data.Event: [commentAddedAuthor] :: CommentAdded -> User
+ Gerrit.Data.Event: [commentAddedChange] :: CommentAdded -> Change
+ Gerrit.Data.Event: [commentAddedEventCreatedOn] :: CommentAdded -> Int64
+ Gerrit.Data.Event: [commentAddedPatchSet] :: CommentAdded -> PatchSet
+ Gerrit.Data.Event: [hashtagsChangedChange] :: HashtagsChanged -> Change
+ Gerrit.Data.Event: [hashtagsChangedEditor] :: HashtagsChanged -> User
+ Gerrit.Data.Event: [hashtagsChangedEventCreatedOn] :: HashtagsChanged -> Int64
+ Gerrit.Data.Event: [patchSetNumber] :: PatchSet -> Int
+ Gerrit.Data.Event: [patchSetRef] :: PatchSet -> Text
+ Gerrit.Data.Event: [patchSetUploader] :: PatchSet -> User
+ Gerrit.Data.Event: [patchsetAuthor] :: PatchSet -> User
+ Gerrit.Data.Event: [patchsetCreatedChange] :: PatchsetCreated -> Change
+ Gerrit.Data.Event: [patchsetCreatedEventCreatedOn] :: PatchsetCreated -> Int64
+ Gerrit.Data.Event: [patchsetCreatedPatchSet] :: PatchsetCreated -> PatchSet
+ Gerrit.Data.Event: [patchsetCreatedUploader] :: PatchsetCreated -> User
+ Gerrit.Data.Event: [privateStateChangedChange] :: PrivateStateChanged -> Change
+ Gerrit.Data.Event: [privateStateChangedChangedEventCreatedOn] :: PrivateStateChanged -> Int64
+ Gerrit.Data.Event: [privateStateChangedChanger] :: PrivateStateChanged -> User
+ Gerrit.Data.Event: [privateStateChangedPatchSet] :: PrivateStateChanged -> PatchSet
+ Gerrit.Data.Event: [projectCreatedEventCreatedOn] :: ProjectCreated -> Int64
+ Gerrit.Data.Event: [projectCreatedHeadName] :: ProjectCreated -> Text
+ Gerrit.Data.Event: [projectCreatedProjectName] :: ProjectCreated -> Text
+ Gerrit.Data.Event: [refNewRev] :: Ref -> Text
+ Gerrit.Data.Event: [refOldRev] :: Ref -> Text
+ Gerrit.Data.Event: [refProject] :: Ref -> Text
+ Gerrit.Data.Event: [refRefName] :: Ref -> Text
+ Gerrit.Data.Event: [refUpdatedEventCreatedOn] :: RefUpdated -> Int64
+ Gerrit.Data.Event: [refUpdatedRefUpdate] :: RefUpdated -> Ref
+ Gerrit.Data.Event: [refUpdatedSubmitter] :: RefUpdated -> User
+ Gerrit.Data.Event: [reviewerAddedAdder] :: ReviewerAdded -> User
+ Gerrit.Data.Event: [reviewerAddedChange] :: ReviewerAdded -> Change
+ Gerrit.Data.Event: [reviewerAddedEventCreatedOn] :: ReviewerAdded -> Int64
+ Gerrit.Data.Event: [reviewerAddedPatchSet] :: ReviewerAdded -> PatchSet
+ Gerrit.Data.Event: [reviewerDeletedChange] :: ReviewerDeleted -> Change
+ Gerrit.Data.Event: [reviewerDeletedEventCreatedOn] :: ReviewerDeleted -> Int64
+ Gerrit.Data.Event: [reviewerDeletedPatchSet] :: ReviewerDeleted -> PatchSet
+ Gerrit.Data.Event: [reviewerDeletedRemover] :: ReviewerDeleted -> User
+ Gerrit.Data.Event: [topicChangedChange] :: TopicChanged -> Change
+ Gerrit.Data.Event: [topicChangedChanger] :: TopicChanged -> User
+ Gerrit.Data.Event: [topicChangedEventCreatedOn] :: TopicChanged -> Int64
+ Gerrit.Data.Event: [topicChangedOldTopic] :: TopicChanged -> Text
+ Gerrit.Data.Event: [userEmail] :: User -> Maybe Text
+ Gerrit.Data.Event: [userName] :: User -> Maybe Text
+ Gerrit.Data.Event: [userUsername] :: User -> Maybe Text
+ Gerrit.Data.Event: [voteDeletedChange] :: VoteDeleted -> Change
+ Gerrit.Data.Event: [voteDeletedPatchSet] :: VoteDeleted -> PatchSet
+ Gerrit.Data.Event: [voteDeletedRemover] :: VoteDeleted -> User
+ Gerrit.Data.Event: [workInProgressStateChangedChange] :: WorkInProgressStateChanged -> Change
+ Gerrit.Data.Event: [workInProgressStateChangedChanger] :: WorkInProgressStateChanged -> User
+ Gerrit.Data.Event: [workInProgressStateChangedEventCreatedOn] :: WorkInProgressStateChanged -> Int64
+ Gerrit.Data.Event: [workInProgressStateChangedPatchSet] :: WorkInProgressStateChanged -> PatchSet
+ Gerrit.Data.Event: data AssigneeChanged
+ Gerrit.Data.Event: data Change
+ Gerrit.Data.Event: data ChangeAbandoned
+ Gerrit.Data.Event: data ChangeDeleted
+ Gerrit.Data.Event: data ChangeMerged
+ Gerrit.Data.Event: data ChangeRestored
+ Gerrit.Data.Event: data CommentAdded
+ Gerrit.Data.Event: data Event
+ Gerrit.Data.Event: data EventType
+ Gerrit.Data.Event: data HashtagsChanged
+ Gerrit.Data.Event: data PatchSet
+ Gerrit.Data.Event: data PatchsetCreated
+ Gerrit.Data.Event: data PrivateStateChanged
+ Gerrit.Data.Event: data ProjectCreated
+ Gerrit.Data.Event: data Ref
+ Gerrit.Data.Event: data RefUpdated
+ Gerrit.Data.Event: data ReviewerAdded
+ Gerrit.Data.Event: data ReviewerDeleted
+ Gerrit.Data.Event: data TopicChanged
+ Gerrit.Data.Event: data User
+ Gerrit.Data.Event: data VoteDeleted
+ Gerrit.Data.Event: data WorkInProgressStateChanged
+ Gerrit.Data.Event: eventName :: EventType -> Text
+ Gerrit.Data.Event: getChange :: Event -> Maybe Change
+ Gerrit.Data.Event: getCreatedOn :: Event -> Maybe Int64
+ Gerrit.Data.Event: getEventType :: Event -> EventType
+ Gerrit.Data.Event: getPatchSet :: Event -> Maybe PatchSet
+ Gerrit.Data.Event: getUser :: Event -> Maybe User
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.AssigneeChanged
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.Change
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ChangeAbandoned
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ChangeDeleted
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ChangeMerged
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ChangeRestored
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.CommentAdded
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.Event
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.EventType
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.HashtagsChanged
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.PatchSet
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.PatchsetCreated
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.PrivateStateChanged
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ProjectCreated
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.Ref
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.RefUpdated
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ReviewerAdded
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.ReviewerDeleted
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.TopicChanged
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.User
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.VoteDeleted
+ Gerrit.Data.Event: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Event.WorkInProgressStateChanged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.AssigneeChanged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.Change
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ChangeAbandoned
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ChangeDeleted
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ChangeMerged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ChangeRestored
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.CommentAdded
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.Event
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.EventType
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.HashtagsChanged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.PatchSet
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.PatchsetCreated
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.PrivateStateChanged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ProjectCreated
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.Ref
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.RefUpdated
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ReviewerAdded
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.ReviewerDeleted
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.TopicChanged
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.User
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.VoteDeleted
+ Gerrit.Data.Event: instance GHC.Classes.Eq Gerrit.Data.Event.WorkInProgressStateChanged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.AssigneeChanged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.Change
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ChangeAbandoned
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ChangeDeleted
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ChangeMerged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ChangeRestored
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.CommentAdded
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.HashtagsChanged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.PatchSet
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.PatchsetCreated
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.PrivateStateChanged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ProjectCreated
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.Ref
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.RefUpdated
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ReviewerAdded
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.ReviewerDeleted
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.TopicChanged
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.User
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.VoteDeleted
+ Gerrit.Data.Event: instance GHC.Generics.Generic Gerrit.Data.Event.WorkInProgressStateChanged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.AssigneeChanged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.Change
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ChangeAbandoned
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ChangeDeleted
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ChangeMerged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ChangeRestored
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.CommentAdded
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.Event
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.EventType
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.HashtagsChanged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.PatchSet
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.PatchsetCreated
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.PrivateStateChanged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ProjectCreated
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.Ref
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.RefUpdated
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ReviewerAdded
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.ReviewerDeleted
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.TopicChanged
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.User
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.VoteDeleted
+ Gerrit.Data.Event: instance GHC.Show.Show Gerrit.Data.Event.WorkInProgressStateChanged
+ Gerrit.Data.Project: GerritProjectInfo :: Text -> GerritProjectInfo
+ Gerrit.Data.Project: Prefix :: Text -> GerritProjectQuery
+ Gerrit.Data.Project: Regexp :: Text -> GerritProjectQuery
+ Gerrit.Data.Project: [gerritprojectinfoId] :: GerritProjectInfo -> Text
+ Gerrit.Data.Project: data GerritProjectQuery
+ Gerrit.Data.Project: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Project.GerritProjectInfo
+ Gerrit.Data.Project: instance GHC.Classes.Eq Gerrit.Data.Project.GerritProjectInfo
+ Gerrit.Data.Project: instance GHC.Generics.Generic Gerrit.Data.Project.GerritProjectInfo
+ Gerrit.Data.Project: instance GHC.Show.Show Gerrit.Data.Project.GerritProjectInfo
+ Gerrit.Data.Project: newtype GerritProjectInfo
+ Gerrit.Data.Project: projectQS :: Int -> GerritProjectQuery -> Maybe Int -> Text
+ Gerrit.Data.Project: type GerritProjectsMessage = Map Text GerritProjectInfo
+ Gerrit.Data.Review: APPROVED :: GerritLabelVote
+ Gerrit.Data.Review: DISLIKED :: GerritLabelVote
+ Gerrit.Data.Review: GerritLabel :: Map GerritLabelVote GerritReviewAccount -> GerritLabel
+ Gerrit.Data.Review: GerritReviewAccount :: Int -> GerritReviewAccount
+ Gerrit.Data.Review: GerritVersion :: Text -> GerritVersion
+ Gerrit.Data.Review: RECOMMENDED :: GerritLabelVote
+ Gerrit.Data.Review: REJECTED :: GerritLabelVote
+ Gerrit.Data.Review: ReviewInput :: Maybe Text -> Maybe (Map Text Int) -> ReviewInput
+ Gerrit.Data.Review: ReviewResult :: Maybe (Map Text Int) -> Maybe Int -> ReviewResult
+ Gerrit.Data.Review: [riLabels] :: ReviewInput -> Maybe (Map Text Int)
+ Gerrit.Data.Review: [riMessage] :: ReviewInput -> Maybe Text
+ Gerrit.Data.Review: [rrLabels] :: ReviewResult -> Maybe (Map Text Int)
+ Gerrit.Data.Review: [rrReady] :: ReviewResult -> Maybe Int
+ Gerrit.Data.Review: [unused_account_id] :: GerritReviewAccount -> Int
+ Gerrit.Data.Review: data GerritLabelVote
+ Gerrit.Data.Review: data ReviewInput
+ Gerrit.Data.Review: data ReviewResult
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.GerritLabel
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.GerritReviewAccount
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.GerritVersion
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSON Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: instance Data.Aeson.Types.FromJSON.FromJSONKey Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance Data.Aeson.Types.ToJSON.ToJSON Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance Data.Aeson.Types.ToJSON.ToJSON Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: instance GHC.Classes.Eq Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance GHC.Classes.Eq Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance GHC.Classes.Eq Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: instance GHC.Classes.Ord Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance GHC.Classes.Ord Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance GHC.Classes.Ord Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.GerritLabel
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.GerritReviewAccount
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.GerritVersion
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance GHC.Generics.Generic Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.GerritLabel
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.GerritLabelVote
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.GerritReviewAccount
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.GerritVersion
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.ReviewInput
+ Gerrit.Data.Review: instance GHC.Show.Show Gerrit.Data.Review.ReviewResult
+ Gerrit.Data.Review: newtype GerritLabel
+ Gerrit.Data.Review: newtype GerritReviewAccount
+ Gerrit.Data.Review: newtype GerritVersion
- Gerrit: GerritChange :: Text -> Text -> Text -> Text -> GerritChangeStatus -> Maybe Bool -> Map Text (Maybe GerritRevision) -> Maybe Text -> Int -> Map Text GerritDetailedLabel -> GerritChange
+ Gerrit: GerritChange :: Text -> Text -> Text -> Text -> GerritChangeStatus -> Maybe Bool -> Map Text (Maybe GerritRevision) -> Maybe Text -> Int -> Map Text GerritDetailedLabel -> [GerritChangeMessage] -> GerritAuthor -> GerritTime -> GerritTime -> Maybe GerritTime -> Maybe GerritAuthor -> Int -> Int -> Maybe Bool -> GerritChange
- Gerrit: queryChanges :: Int -> [GerritQuery] -> GerritClient -> IO [GerritChange]
+ Gerrit: queryChanges :: Int -> [GerritQuery] -> Maybe Int -> GerritClient -> IO [GerritChange]
- Gerrit: withClient :: Text -> Maybe Text -> (GerritClient -> IO ()) -> IO ()
+ Gerrit: withClient :: Text -> Maybe (Text, Text) -> (GerritClient -> IO a) -> IO a

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog +## 0.1.1.0 (2021-10-13)++- Add EventCreatedOn Event attribute+- Split Data module into specialized modules+- Support more fields for GerritChange+- Add the getProjects function+- withClient takes optional credentials+ ## 0.1.0.0 (2021-05-30)  - Basic Gerrit API data types.
gerrit.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                gerrit-version:             0.1.0.0+version:             0.1.1.0 synopsis:            A gerrit client library description:     Gerrit is a client library to interface with https://www.gerritcodereview.com/@@ -29,6 +29,7 @@   build-depends:       base                   >= 4.11.0.0 && < 5                      , aeson                  >= 1.0.0.0  && < 1.6                      , bytestring             < 0.11+                     , time                   >= 1.9.3    && < 2.0   ghc-options:         -Wall                        -Wcompat                        -Widentities@@ -49,13 +50,17 @@   import:              common-options   build-depends:       containers             >= 0.5.8.0  && < 0.7                      , http-client            >= 0.5.0    && < 0.8-                     , http-client-tls        >= 0.2.0    && < 0.4+                     , http-client-openssl    >= 0.3.0    && < 0.4                      , text                   >= 0.11.1.0 && < 1.3+                     , aeson-casing           >= 0.2.0.0  && < 0.3.0.0    hs-source-dirs:      src   exposed-modules:     Gerrit-                     , Gerrit.Data-                     , Gerrit.Event+                     , Gerrit.Data.Review+                     , Gerrit.Data.Change+                     , Gerrit.Data.Account+                     , Gerrit.Data.Event+                     , Gerrit.Data.Project   other-modules:       Gerrit.Client  test-suite gerrit-test
src/Gerrit.hs view
@@ -5,12 +5,16 @@   ( -- * Client     GerritClient,     withClient,+    getClient,      -- * Api     getVersion,     getChange,     queryChanges,     postReview,+    getAccountId,+    getAccount,+    getProjects,      -- * Main data types     GerritVersion (..),@@ -18,22 +22,33 @@     GerritChange (..),     GerritChangeStatus (..),     ReviewResult (..),+    GerritAccount (..),+    GerritAccountQuery (..),+    GerritProjectQuery (..),      -- * Convenient functions     changeUrl,-    hasLabel,+    serverUrl,   ) where  import Control.Exception (try)+import Data.List.NonEmpty (NonEmpty) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T import Gerrit.Client-import Gerrit.Data+import Gerrit.Data.Account+import Gerrit.Data.Change+import Gerrit.Data.Project+import Gerrit.Data.Review import Network.HTTP.Client (HttpException) +-- | Return the base url of the 'GerritClient'+serverUrl :: GerritClient -> T.Text+serverUrl = baseUrl+ -- | Return the url of a 'GerritChange' changeUrl :: GerritClient -> GerritChange -> T.Text changeUrl client change = baseUrl client <> T.pack (show (number change))@@ -42,20 +57,38 @@ getVersion :: GerritClient -> IO GerritVersion getVersion = gerritGet "config/server/version" +-- | Get projects+getProjects ::+  -- | Count of projects to get back+  Int ->+  -- | The project query type+  GerritProjectQuery ->+  -- | Whether or not to ask result from offset+  Maybe Int ->+  -- | The client+  GerritClient ->+  IO GerritProjectsMessage+getProjects count query startM = gerritGet ("projects/?" <> projectQS count query startM)+ -- | Search for changes-queryChanges :: Int -> [GerritQuery] -> GerritClient -> IO [GerritChange]-queryChanges count queries = gerritGet ("changes/?" <> queryString)-  where-    queryString = T.intercalate "&" [changeString, countString, option]-    changeString = "q=" <> T.intercalate "+" (map queryText queries)-    countString = "n=" <> T.pack (show count)-    option = "o=CURRENT_REVISION&o=DETAILED_LABELS"+queryChanges ::+  -- | Count of changes to get back+  Int ->+  -- | The change query+  [GerritQuery] ->+  -- | Whether or not to ask result from offset+  Maybe Int ->+  -- | The client+  GerritClient ->+  IO [GerritChange]+queryChanges count queries startM = gerritGet ("changes/?" <> changeQS count queries startM) +-- | Get a change by change Id getChange :: Int -> GerritClient -> IO (Maybe GerritChange) getChange changeNumber client = do   res <-     try-      ( gerritGet ("changes/" <> T.pack (show changeNumber) <> "?o=CURRENT_REVISION&o=DETAILED_LABELS") client+      ( gerritGet ("changes/" <> T.pack (show changeNumber) <> "?" <> defaultQueryChangeOptions) client       ) ::       IO (Either HttpException GerritChange)   pure $ case res of@@ -79,18 +112,18 @@ postReview change message label value' = gerritPost urlPath review   where     urlPath = "changes/" <> changeId <> "/revisions/" <> revHash <> "/review"-    changeId = Gerrit.Data.id change-    revHash = fromMaybe "" (Gerrit.Data.current_revision change)+    changeId = Gerrit.Data.Change.id change+    revHash = fromMaybe "" (Gerrit.Data.Change.current_revision change)     review =       ReviewInput         { riMessage = Just message,           riLabels = Just (M.fromList [(label, value')])         } --- | Check if a gerrit change as a label-hasLabel :: T.Text -> Int -> GerritChange -> Bool-hasLabel label labelValue change = case M.lookup label (labels change) of-  Just gerritLabel ->-    (> 0) $-      length $ filter (\vote -> fromMaybe 0 (value vote) == labelValue) (Gerrit.Data.all gerritLabel)-  _ -> False+-- | Get user account id+getAccountId :: Int -> NonEmpty GerritAccountQuery -> GerritClient -> IO [GerritAccountId]+getAccountId count queries = gerritGet ("accounts/?" <> accountQs count queries)++-- | Get user account details+getAccount :: Int -> NonEmpty GerritAccountQuery -> GerritClient -> IO [GerritAccount]+getAccount count queries = gerritGet ("accounts/?" <> accountQs count queries <> "&o=DETAILS")
src/Gerrit/Client.hs view
@@ -7,18 +7,17 @@     withClient,     gerritGet,     gerritPost,+    getClient,   ) where  import Data.Aeson (FromJSON, ToJSON, eitherDecode, encode) import qualified Data.ByteString.Lazy as BSL-import Data.Maybe (fromMaybe) import Data.Text (Text, unpack) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Network.HTTP.Client-import Network.HTTP.Client.TLS (tlsManagerSettings)-import System.Environment (lookupEnv)+import Network.HTTP.Client.OpenSSL (newOpenSSLManager, withOpenSSL)  -- | The GerritClient record, use 'withClient' to create data GerritClient = GerritClient@@ -27,27 +26,26 @@     auth :: Maybe (Text, Text)   } +-- | Need to be call through withOpenSSL+getClient :: Text -> Maybe (Text, Text) -> IO GerritClient+getClient url auth = do+  let baseUrl = T.dropWhileEnd (== '/') url <> "/"+  manager <- newOpenSSLManager+  pure $ GerritClient {..}+ -- | Create the 'GerritClient' withClient ::   -- | The gerrit api url   Text ->-  -- | A username (password is read from GERRIT_PASSWORD environment)-  Maybe Text ->+  -- | Credentials (login, password) [Optional]+  Maybe (Text, Text) ->   -- | The callback-  (GerritClient -> IO ()) ->+  (GerritClient -> IO a) ->   -- | withClient performs the IO-  IO ()-withClient url username callBack =-  do-    manager <- newManager tlsManagerSettings-    auth <- case username of-      Just user -> do-        pass <- lookupEnv "GERRIT_PASSWORD"-        pure $ Just (user, T.pack $ fromMaybe "" pass)-      _ -> pure Nothing-    callBack (GerritClient {..})-  where-    baseUrl = T.dropWhileEnd (== '/') url <> "/"+  IO a+withClient url creds callBack = withOpenSSL $ do+  client <- getClient url creds+  callBack client  gerritDecode :: (FromJSON a, Applicative f) => Response BSL.ByteString -> f a gerritDecode response = case eitherDecode $ BSL.drop 5 $ responseBody response of@@ -74,6 +72,7 @@ gerritGet :: (FromJSON a) => Text -> GerritClient -> IO a gerritGet path GerritClient {..} =   do-    request <- parseUrlThrow (unpack $ baseUrl <> path)+    let url = baseUrl <> path+    request <- parseUrlThrow (unpack url)     response <- httpLbs request manager     gerritDecode response
− src/Gerrit/Data.hs
@@ -1,168 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE OverloadedStrings #-}---- | This module contains the gerrit data type-module Gerrit.Data-  ( -- * Main data types-    GerritVersion (..),-    GerritQuery (..),-    GerritChangeStatus (..),-    GerritChange (..),-    GerritRevision (..),-    GerritLabel (..),-    GerritAccount (..),-    GerritLabelVote (..),-    GerritDetailedLabelVote (..),-    GerritDetailedLabel (..),-    ReviewResult (..),-    ReviewInput (..),--    -- * Convenient functions-    queryText,-  )-where--import Data.Aeson-import Data.Char (isUpper, toLower)-import qualified Data.Map as M-import Data.Text (Text)-import qualified Data.Text as T-import GHC.Generics (Generic)--newtype GerritVersion = GerritVersion Text-  deriving (Show, Generic)-  deriving anyclass (FromJSON)---- https://gerrit-review.googlesource.com/Documentation/json.html-data GerritRevisionKind = REWORK | TRIVIAL_REBASE | MERGE_FIRST_PARENT_UPDATE | NO_CODE_CHANGE | NO_CHANGE-  deriving (Eq, Show, Generic, FromJSON)--data GerritChangeStatus = NEW | MERGED | ABANDONED | DRAFT-  deriving (Eq, Show, Generic, FromJSON)--data GerritLabelVote = REJECTED | APPROVED | DISLIKED | RECOMMENDED-  deriving (Eq, Show, Ord, Generic)---- We use a custom parseJSON to decode Label Vote as lowercase-instance FromJSON GerritLabelVote where-  parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = map toLower}--instance FromJSONKey GerritLabelVote where-  fromJSONKey = genericFromJSONKey defaultJSONKeyOptions {keyModifier = map toLower}---- | Modify record attribute to match json schema--- Remove the prefix and use snakecase-customParseJSON :: String -> Options-customParseJSON prefix = defaultOptions {fieldLabelModifier = recordToJson}-  where-    recordToJson = updateCase . drop (length prefix)-    updateCase [] = []-    updateCase (x : xs) = toLower x : updateCase' xs-    updateCase' [] = []-    updateCase' (x : xs)-      | isUpper x = '_' : toLower x : updateCase' xs-      | otherwise = x : updateCase' xs---- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html-data ReviewResult = ReviewResult-  { rrLabels :: Maybe (M.Map Text Int),-    rrReady :: Maybe Int-  }-  deriving (Eq, Show, Ord, Generic)--instance FromJSON ReviewResult where-  parseJSON = genericParseJSON $ customParseJSON "rr"--instance ToJSON ReviewResult where-  toJSON = genericToJSON $ customParseJSON "rr"--data ReviewInput = ReviewInput-  { riMessage :: Maybe Text,-    riLabels :: Maybe (M.Map Text Int)-  }-  deriving (Eq, Show, Ord, Generic)--instance FromJSON ReviewInput where-  parseJSON = genericParseJSON $ customParseJSON "ri"--instance ToJSON ReviewInput where-  toJSON = genericToJSON $ (customParseJSON "ri") {omitNothingFields = True}---- https://gerrit-review.googlesource.com/Documentation/user-search.html-data GerritQuery-  = Status GerritChangeStatus-  | Owner Text-  | CommitMessage Text-  | Project Text-  | ChangeId Text---- | Convert a GerritQuery object to the search terms-queryText :: GerritQuery -> Text-queryText (Status stat) = "status:" <> T.toLower (T.pack $ show stat)-queryText (Owner owner) = "owner:" <> owner-queryText (CommitMessage message) = "message:" <> message-queryText (Project project') = "project:" <> project'-queryText (ChangeId changeId) = "change:" <> changeId--data GerritRevision = GerritRevision-  { ref :: Text,-    kind :: GerritRevisionKind-  }-  deriving (Show, Generic, FromJSON)--newtype GerritAccount = GerritAccount-  { unused_account_id :: Int-  }-  deriving (Show, Generic)---- We use a cusom parseJSON to decode `_account_id` as `account_id`-instance FromJSON GerritAccount where-  parseJSON = genericParseJSON aesonOptions--newtype GerritLabel-  = GerritLabel (M.Map GerritLabelVote GerritAccount)-  deriving (Show, Generic)-  deriving anyclass (FromJSON)--data GerritDetailedLabelVote = GerritDetailedLabelVote-  { value :: Maybe Int,-    account_id :: Int-  }-  deriving (Show, Generic)---- We use a cusom parseJSON to decode record field properly-instance FromJSON GerritDetailedLabelVote where-  parseJSON = genericParseJSON aesonOptions--data GerritDetailedLabel = GerritDetailedLabel-  { all :: [GerritDetailedLabelVote],-    default_value :: Int-  }-  deriving (Show, Generic, FromJSON)--data GerritChange = GerritChange-  { id :: Text,-    project :: Text,-    branch :: Text,-    subject :: Text,-    status :: GerritChangeStatus,-    mergeable :: Maybe Bool,-    revisions :: M.Map Text (Maybe GerritRevision),-    current_revision :: Maybe Text,-    number :: Int,-    labels :: M.Map Text GerritDetailedLabel-  }-  deriving (Show, Generic)---- We use a cusom parseJSON to decode `_number` as `number`-instance FromJSON GerritChange where-  parseJSON = genericParseJSON aesonOptions--aesonOptions :: Options-aesonOptions = defaultOptions {fieldLabelModifier = recordToJson}-  where-    recordToJson "number" = "_number"-    recordToJson "account_id" = "_account_id"-    recordToJson n = n
+ src/Gerrit/Data/Account.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}++module Gerrit.Data.Account+  ( GerritAccountId (..),+    GerritAccount (..),+    GerritAccountQuery (..),+    userQueryText,+    accountQs,+  )+where++import Control.Monad (mzero)+import Data.Aeson+import Data.List.NonEmpty (NonEmpty, toList)+import Data.Text (Text)+import qualified Data.Text as T++-- https://gerrit-review.googlesource.com/Documentation/user-search-accounts.html#_search_operators+data GerritAccountQuery+  = CanSee Text+  | Email Text+  | Name Text+  | Username Text+  | IsActive+  | IsInactive++userQueryText :: GerritAccountQuery -> Text+userQueryText guq = case guq of+  CanSee change -> "cansee:" <> change+  Email email -> "email:" <> email+  Name name -> "name:" <> escapeChar name+  Username username -> "username:" <> username+  IsActive -> "is:active"+  IsInactive -> "is:inactive"+  where+    escapeChar = T.replace "'" " "++accountQs :: Int -> NonEmpty GerritAccountQuery -> Text+accountQs count queries = T.intercalate "&" [searchString, countString]+  where+    searchString = "q=" <> T.intercalate "+" (map userQueryText $ toList queries)+    countString = "n=" <> T.pack (show count)++data GerritAccountId = GerritAccountId+  { gerritAccountId' :: Int,+    gerritAccountHasMore' :: Maybe Bool+  }+  deriving (Eq, Show)++instance FromJSON GerritAccountId where+  parseJSON (Object v) = GerritAccountId <$> v .: "_account_id" <*> v .:? "_more_accounts"+  parseJSON _ = mzero++data GerritAccount = GerritAccount+  { gerritAccountId :: Int,+    gerritAccountName :: Text,+    gerritAccountUsername :: Maybe Text,+    gerritAccountEmail :: Maybe Text,+    gerritAccountHasMore :: Maybe Bool+  }+  deriving (Eq, Show)++instance FromJSON GerritAccount where+  parseJSON (Object v) =+    GerritAccount+      <$> v .: "_account_id"+      <*> v .: "name"+      <*> v .:? "username"+      <*> v .:? "email"+      <*> v .:? "_more_accounts"+  parseJSON _ = mzero
+ src/Gerrit/Data/Change.hs view
@@ -0,0 +1,235 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}++module Gerrit.Data.Change+  ( GerritQuery (..),+    GerritChangeStatus (..),+    GerritRevisionKind (..),+    GerritFile (..),+    GerritCommit (..),+    GerritRevision (..),+    GerritDetailedLabelVote (..),+    GerritDetailedLabel (..),+    GerritAuthor (..),+    GerritCommitAuthor (..),+    GerritChangeMessage (..),+    GerritChange (..),+    GerritTime (..),+    changeQS,+    queryText,+    defaultQueryChangeOptions,+    hasLabel,+  )+where++import Data.Aeson+import Data.Aeson.Casing (aesonPrefix, snakeCase)+import qualified Data.Map as M+import Data.Maybe (fromMaybe)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Time.Clock+import Data.Time.Format (defaultTimeLocale, formatTime, parseTimeM)+import GHC.Generics (Generic)++aesonOptions :: Options+aesonOptions = defaultOptions {fieldLabelModifier = recordToJson}+  where+    recordToJson "number" = "_number"+    recordToJson "account_id" = "_account_id"+    recordToJson "aAccountId" = "_account_id"+    recordToJson "aName" = "name"+    recordToJson "aEmail" = "email"+    recordToJson "aUsername" = "username"+    recordToJson "more_changes" = "_more_changes"+    recordToJson n = n++-- https://gerrit-review.googlesource.com/Documentation/user-search.html+data GerritQuery+  = Status GerritChangeStatus+  | Owner Text+  | CommitMessage Text+  | Project Text+  | ChangeId Text+  | After UTCTime++-- | Convert a GerritQuery object to the search terms+queryText :: GerritQuery -> Text+queryText (Status stat) = "status:" <> T.toLower (T.pack $ show stat)+queryText (Owner owner') = "owner:" <> owner'+queryText (CommitMessage message) = "message:" <> message+queryText (Project project') = "project:" <> project'+queryText (ChangeId changeId) = "change:" <> changeId+queryText (After date') = "after:" <> T.pack formatedDate+  where+    formatedDate = formatTime defaultTimeLocale "%F" date'++defaultQueryChangeOptions :: Text+defaultQueryChangeOptions =+  "o="+    <> T.intercalate+      "&o="+      [ "MESSAGES",+        "DETAILED_ACCOUNTS",+        "DETAILED_LABELS",+        "CURRENT_REVISION",+        "CURRENT_FILES",+        "CURRENT_COMMIT"+      ]++-- | Build the Query String for the changes endpoint+-- >>> changeQS 10 [Project "software-factory/gerrit-haskell"] Nothing+-- "q=project:software-factory/gerrit-haskell&n=10&o=MESSAGES&o=DETAILED_ACCOUNTS&o=DETAILED_LABELS&o=CURRENT_REVISION&o=CURRENT_FILES&o=CURRENT_COMMIT"+-- >>> changeQS 10 [Project "software-factory/gerrit-haskell"] $ Just 100+-- "q=project:software-factory/gerrit-haskell&n=10&o=MESSAGES&o=DETAILED_ACCOUNTS&o=DETAILED_LABELS&o=CURRENT_REVISION&o=CURRENT_FILES&o=CURRENT_COMMIT&start=100"+changeQS :: Int -> [GerritQuery] -> Maybe Int -> Text+changeQS count queries startM =+  let base =+        T.intercalate+          "&"+          [ changeString,+            countString,+            defaultQueryChangeOptions+          ]+   in base <> startString+  where+    changeString = "q=" <> T.intercalate "+" (map queryText queries)+    countString = "n=" <> T.pack (show count)+    startString = maybe mempty (\s -> "&start=" <> T.pack (show s)) startM++-- | Check if a gerrit change as a label+hasLabel :: T.Text -> Int -> GerritChange -> Bool+hasLabel label labelValue change = case M.lookup label (labels change) of+  Just gerritLabel ->+    (> 0) $+      length $+        filter+          (\vote -> fromMaybe 0 (value vote) == labelValue)+          (fromMaybe [] (Gerrit.Data.Change.all gerritLabel))+  _ -> False++data GerritChangeStatus = NEW | MERGED | ABANDONED | DRAFT+  deriving (Eq, Show, Generic, FromJSON)++-- https://gerrit-review.googlesource.com/Documentation/json.html+data GerritRevisionKind = REWORK | TRIVIAL_REBASE | MERGE_FIRST_PARENT_UPDATE | NO_CODE_CHANGE | NO_CHANGE+  deriving (Eq, Show, Generic, FromJSON)++data GerritFile = GerritFile+  { gfStatus :: Maybe Text,+    gfLinesInserted :: Maybe Int,+    gfLinesDeleted :: Maybe Int,+    gfSizeDelta :: Maybe Int,+    gfSize :: Maybe Int+  }+  deriving (Show, Generic)++instance FromJSON GerritFile where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase++data GerritCommit = GerritCommit+  { cAuthor :: GerritCommitAuthor,+    cCommitter :: GerritCommitAuthor,+    cSubject :: Text,+    cMessage :: Text+  }+  deriving (Show, Generic)++instance FromJSON GerritCommit where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase++data GerritRevision = GerritRevision+  { grRef :: Text,+    grKind :: GerritRevisionKind,+    grFiles :: M.Map Text GerritFile,+    grCommit :: GerritCommit,+    grUploader :: GerritAuthor+  }+  deriving (Show, Generic)++instance FromJSON GerritRevision where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase++data GerritDetailedLabelVote = GerritDetailedLabelVote+  { value :: Maybe Int,+    account_id :: Int+  }+  deriving (Show, Generic)++instance FromJSON GerritDetailedLabelVote where+  parseJSON = genericParseJSON aesonOptions++data GerritDetailedLabel = GerritDetailedLabel+  { all :: Maybe [GerritDetailedLabelVote],+    default_value :: Int+  }+  deriving (Show, Generic, FromJSON)++data GerritAuthor = GerritAuthor+  { aAccountId :: Int,+    aName :: Text,+    aEmail :: Maybe Text,+    aUsername :: Maybe Text+  }+  deriving (Show, Generic)++instance FromJSON GerritAuthor where+  parseJSON = genericParseJSON aesonOptions++data GerritCommitAuthor = GerritCommitAuthor+  { caName :: Text,+    caEmail :: Maybe Text,+    caDate :: GerritTime+  }+  deriving (Show, Generic)++instance FromJSON GerritCommitAuthor where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase++newtype GerritTime = GerritTime {unGerritTime :: UTCTime} deriving (Show)++instance FromJSON GerritTime where+  parseJSON = withText "UTCTimePlus" (parse . T.unpack)+    where+      format = "%F %T.000000000"+      tryParse f s = parseTimeM False defaultTimeLocale f s+      parse s = GerritTime <$> tryParse format s++data GerritChangeMessage = GerritChangeMessage+  { mId :: Text,+    mAuthor :: Maybe GerritAuthor,+    mDate :: GerritTime,+    mMessage :: Text+  }+  deriving (Show, Generic)++instance FromJSON GerritChangeMessage where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase++data GerritChange = GerritChange+  { id :: Text,+    project :: Text,+    branch :: Text,+    subject :: Text,+    status :: GerritChangeStatus,+    mergeable :: Maybe Bool,+    revisions :: M.Map Text (Maybe GerritRevision),+    current_revision :: Maybe Text,+    number :: Int,+    labels :: M.Map Text GerritDetailedLabel,+    messages :: [GerritChangeMessage],+    owner :: GerritAuthor,+    created :: GerritTime,+    updated :: GerritTime,+    submitted :: Maybe GerritTime,+    submitter :: Maybe GerritAuthor,+    insertions :: Int,+    deletions :: Int,+    more_changes :: Maybe Bool+  }+  deriving (Show, Generic)++instance FromJSON GerritChange where+  parseJSON = genericParseJSON aesonOptions
+ src/Gerrit/Data/Event.hs view
@@ -0,0 +1,509 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++-- | Gerrit stream event data type+module Gerrit.Data.Event+  ( -- * Main event data types+    EventType (..),+    Event (..),++    -- * Common data types+    User (..),+    PatchSet (..),+    Change (..),+    Ref (..),++    -- * Individual event data types+    AssigneeChanged (..),+    ChangeAbandoned (..),+    ChangeDeleted (..),+    ChangeMerged (..),+    ChangeRestored (..),+    CommentAdded (..),+    HashtagsChanged (..),+    ProjectCreated (..),+    PatchsetCreated (..),+    RefUpdated (..),+    ReviewerAdded (..),+    ReviewerDeleted (..),+    TopicChanged (..),+    WorkInProgressStateChanged (..),+    PrivateStateChanged (..),+    VoteDeleted (..),++    -- * Convenient functions+    getChange,+    getPatchSet,+    getUser,+    getCreatedOn,+    getEventType,+    eventName,+  )+where++import Control.Monad (mzero)+import Data.Aeson+  ( FromJSON (..),+    Options (fieldLabelModifier),+    Value (Object, String),+    defaultOptions,+    genericParseJSON,+    (.:),+  )+import Data.Char (toLower)+import Data.Text (Text)+import qualified Data.Text as Text+import GHC.Generics (Generic)+import GHC.Int (Int64)++aesonOption :: Text -> Options+aesonOption prefix = defaultOptions {fieldLabelModifier = recordToJson}+  where+    recordToJson = updateCase . drop (Text.length prefix)+    updateCase [] = []+    updateCase (x : xs) = toLower x : xs++-------------------------------------------------------------------------------+-- Shared types+-------------------------------------------------------------------------------+data User = User+  { userName :: Maybe Text,+    userEmail :: Maybe Text,+    userUsername :: Maybe Text+  }+  deriving (Show, Eq, Generic)++instance FromJSON User where+  parseJSON = genericParseJSON $ aesonOption "user"++data PatchSet = PatchSet+  { patchSetNumber :: Int,+    patchSetRef :: Text,+    patchSetUploader :: User,+    patchsetAuthor :: User+  }+  deriving (Show, Eq, Generic)++instance FromJSON PatchSet where+  parseJSON = genericParseJSON $ aesonOption "patchSet"++data Change = Change+  { changeProject :: Text,+    changeBranch :: Text,+    changeSubject :: Text,+    changeNumber :: Int,+    changeUrl :: Text,+    changeOwner :: User+  }+  deriving (Show, Eq, Generic)++instance FromJSON Change where+  parseJSON = genericParseJSON $ aesonOption "change"++data Ref = Ref+  { refOldRev :: Text,+    refNewRev :: Text,+    refRefName :: Text,+    refProject :: Text+  }+  deriving (Show, Eq, Generic)++instance FromJSON Ref where+  parseJSON = genericParseJSON $ aesonOption "ref"++-------------------------------------------------------------------------------+-- Individual Event data types+-------------------------------------------------------------------------------+data AssigneeChanged = AssigneeChanged+  { assigneeChangedChange :: Change,+    assigneeChangedChanger :: User,+    assigneeChangedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ChangeAbandoned = ChangeAbandoned+  { changeAbandonedProject :: Text,+    changeAbandonedAbandoner :: User,+    changeAbandonedChange :: Change,+    changeAbandonedPatchSet :: PatchSet,+    changeAbandonedReason :: Maybe Text,+    changeAbandonedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ChangeDeleted = ChangeDeleted+  { changeDeletedChange :: Change,+    changeDeletedDeleter :: User+  }+  deriving (Show, Eq, Generic)++data ChangeMerged = ChangeMerged+  { changeMergedProject :: Text,+    changeMergedSubmitter :: User,+    changeMergedChange :: Change,+    changeMergedPatchSet :: PatchSet,+    changeMergedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ChangeRestored = ChangeRestored+  { changeRestoredChange :: Change,+    changeRestoredPatchSet :: PatchSet,+    changeRestoredRestorer :: User,+    changeRestoredReason :: Maybe Text,+    changeRestoredEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data CommentAdded = CommentAdded+  { commentAddedChange :: Change,+    commentAddedPatchSet :: PatchSet,+    commentAddedAuthor :: User,+    commentAddedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data HashtagsChanged = HashtagsChanged+  { hashtagsChangedChange :: Change,+    hashtagsChangedEditor :: User,+    hashtagsChangedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ProjectCreated = ProjectCreated+  { projectCreatedProjectName :: Text,+    projectCreatedHeadName :: Text,+    projectCreatedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data PatchsetCreated = PatchsetCreated+  { patchsetCreatedUploader :: User,+    patchsetCreatedChange :: Change,+    patchsetCreatedPatchSet :: PatchSet,+    patchsetCreatedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data RefUpdated = RefUpdated+  { refUpdatedSubmitter :: User,+    refUpdatedRefUpdate :: Ref,+    refUpdatedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ReviewerAdded = ReviewerAdded+  { reviewerAddedChange :: Change,+    reviewerAddedPatchSet :: PatchSet,+    reviewerAddedAdder :: User,+    reviewerAddedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data ReviewerDeleted = ReviewerDeleted+  { reviewerDeletedChange :: Change,+    reviewerDeletedPatchSet :: PatchSet,+    reviewerDeletedRemover :: User,+    reviewerDeletedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data TopicChanged = TopicChanged+  { topicChangedChange :: Change,+    topicChangedChanger :: User,+    topicChangedOldTopic :: Text,+    topicChangedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data WorkInProgressStateChanged = WorkInProgressStateChanged+  { workInProgressStateChangedChange :: Change,+    workInProgressStateChangedPatchSet :: PatchSet,+    workInProgressStateChangedChanger :: User,+    workInProgressStateChangedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data PrivateStateChanged = PrivateStateChanged+  { privateStateChangedChange :: Change,+    privateStateChangedPatchSet :: PatchSet,+    privateStateChangedChanger :: User,+    privateStateChangedChangedEventCreatedOn :: Int64+  }+  deriving (Show, Eq, Generic)++data VoteDeleted = VoteDeleted+  { voteDeletedChange :: Change,+    voteDeletedPatchSet :: PatchSet,+    voteDeletedRemover :: User+  }+  deriving (Show, Eq, Generic)++-------------------------------------------------------------------------------+-- Events FromJSON instances+-------------------------------------------------------------------------------+instance FromJSON AssigneeChanged where+  parseJSON = genericParseJSON $ aesonOption "assigneeChanged"++instance FromJSON ChangeAbandoned where+  parseJSON = genericParseJSON $ aesonOption "changeAbandoned"++instance FromJSON ChangeDeleted where+  parseJSON = genericParseJSON $ aesonOption "changeDeleted"++instance FromJSON ChangeMerged where+  parseJSON = genericParseJSON $ aesonOption "changeMerged"++instance FromJSON ChangeRestored where+  parseJSON = genericParseJSON $ aesonOption "changeRestored"++instance FromJSON CommentAdded where+  parseJSON = genericParseJSON $ aesonOption "commentAdded"++instance FromJSON HashtagsChanged where+  parseJSON = genericParseJSON $ aesonOption "hashtagsChanged"++instance FromJSON ProjectCreated where+  parseJSON = genericParseJSON $ aesonOption "projectCreated"++instance FromJSON PatchsetCreated where+  parseJSON = genericParseJSON $ aesonOption "patchsetCreated"++instance FromJSON RefUpdated where+  parseJSON = genericParseJSON $ aesonOption "refUpdated"++instance FromJSON ReviewerAdded where+  parseJSON = genericParseJSON $ aesonOption "reviewerAdded"++instance FromJSON ReviewerDeleted where+  parseJSON = genericParseJSON $ aesonOption "reviewerDeleted"++instance FromJSON TopicChanged where+  parseJSON = genericParseJSON $ aesonOption "topicChanged"++instance FromJSON WorkInProgressStateChanged where+  parseJSON = genericParseJSON $ aesonOption "workInProgressStateChanged"++instance FromJSON PrivateStateChanged where+  parseJSON = genericParseJSON $ aesonOption "privateStateChanged"++instance FromJSON VoteDeleted where+  parseJSON = genericParseJSON $ aesonOption "voteDeleted"++-- | The event "type" data type+data EventType+  = AssigneeChangedEvent+  | ChangeAbandonedEvent+  | ChangeDeletedEvent+  | ChangeMergedEvent+  | ChangeRestoredEvent+  | CommentAddedEvent+  | DroppedOutputEvent+  | HashtagsChangedEvent+  | ProjectCreatedEvent+  | PatchsetCreatedEvent+  | RefUpdatedEvent+  | ReviewerAddedEvent+  | ReviewerDeletedEvent+  | TopicChangedEvent+  | WorkInProgressStateChangedEvent+  | PrivateStateChangedEvent+  | VoteDeletedEvent+  deriving (Show, Eq)++instance FromJSON EventType where+  parseJSON (String v) = case v of+    "assignee-changed" -> pure AssigneeChangedEvent+    "change-abandoned" -> pure ChangeAbandonedEvent+    "change-deleted" -> pure ChangeDeletedEvent+    "change-merged" -> pure ChangeMergedEvent+    "change-restored" -> pure ChangeRestoredEvent+    "comment-added" -> pure CommentAddedEvent+    "dropped-output" -> pure DroppedOutputEvent+    "hashtags-changed" -> pure HashtagsChangedEvent+    "project-created" -> pure ProjectCreatedEvent+    "patchset-created" -> pure PatchsetCreatedEvent+    "ref-updated" -> pure RefUpdatedEvent+    "reviewer-added" -> pure ReviewerAddedEvent+    "reviewer-deleted" -> pure ReviewerDeletedEvent+    "topic-changed" -> pure TopicChangedEvent+    "wip-state-changed" -> pure WorkInProgressStateChangedEvent+    "private-state-changed" -> pure PrivateStateChangedEvent+    "vote-deleted" -> pure VoteDeletedEvent+    _ -> mzero+  parseJSON _ = mzero++-- | The main event union data type+data Event+  = EventAssigneeChanged AssigneeChanged+  | EventChangeAbandoned ChangeAbandoned+  | EventChangeDeleted ChangeDeleted+  | EventChangeMerged ChangeMerged+  | EventChangeRestored ChangeRestored+  | EventCommentAdded CommentAdded+  | EventDroppedOutput+  | EventHashtagsChanged HashtagsChanged+  | EventProjectCreated ProjectCreated+  | EventPatchsetCreated PatchsetCreated+  | EventRefUpdated RefUpdated+  | EventReviewerAdded ReviewerAdded+  | EventReviewerDeleted ReviewerDeleted+  | EventTopicChanged TopicChanged+  | EventWorkInProgressStateChanged WorkInProgressStateChanged+  | EventPrivateStateChanged PrivateStateChanged+  | EventVoteDeleted VoteDeleted+  deriving (Show, Eq)++instance FromJSON Event where+  parseJSON o@(Object v) = do+    eType <- v .: "type"+    case eType of+      AssigneeChangedEvent -> EventAssigneeChanged <$> parseJSON o+      ChangeAbandonedEvent -> EventChangeAbandoned <$> parseJSON o+      ChangeDeletedEvent -> EventChangeDeleted <$> parseJSON o+      ChangeMergedEvent -> EventChangeMerged <$> parseJSON o+      ChangeRestoredEvent -> EventChangeRestored <$> parseJSON o+      CommentAddedEvent -> EventCommentAdded <$> parseJSON o+      DroppedOutputEvent -> pure EventDroppedOutput+      HashtagsChangedEvent -> EventHashtagsChanged <$> parseJSON o+      ProjectCreatedEvent -> EventProjectCreated <$> parseJSON o+      PatchsetCreatedEvent -> EventPatchsetCreated <$> parseJSON o+      RefUpdatedEvent -> EventRefUpdated <$> parseJSON o+      ReviewerAddedEvent -> EventReviewerAdded <$> parseJSON o+      ReviewerDeletedEvent -> EventReviewerDeleted <$> parseJSON o+      TopicChangedEvent -> EventTopicChanged <$> parseJSON o+      WorkInProgressStateChangedEvent -> EventWorkInProgressStateChanged <$> parseJSON o+      PrivateStateChangedEvent -> EventPrivateStateChanged <$> parseJSON o+      VoteDeletedEvent -> EventVoteDeleted <$> parseJSON o+  parseJSON _ = mzero++-------------------------------------------------------------------------------+-- Utilities function+-------------------------------------------------------------------------------++-- | Get the associated change+getChange :: Event -> Maybe Change+getChange event = case event of+  EventAssigneeChanged AssigneeChanged {..} -> Just assigneeChangedChange+  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedChange+  EventChangeDeleted ChangeDeleted {..} -> Just changeDeletedChange+  EventChangeMerged ChangeMerged {..} -> Just changeMergedChange+  EventChangeRestored ChangeRestored {..} -> Just changeRestoredChange+  EventCommentAdded CommentAdded {..} -> Just commentAddedChange+  EventDroppedOutput -> Nothing+  EventHashtagsChanged HashtagsChanged {..} -> Just hashtagsChangedChange+  EventProjectCreated _ -> Nothing+  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedChange+  EventRefUpdated _ -> Nothing+  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedChange+  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedChange+  EventTopicChanged TopicChanged {..} -> Just topicChangedChange+  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedChange+  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedChange+  EventVoteDeleted VoteDeleted {..} -> Just voteDeletedChange++-- | Get the associated patchset+getPatchSet :: Event -> Maybe PatchSet+getPatchSet event = case event of+  EventAssigneeChanged _ -> Nothing+  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedPatchSet+  EventChangeDeleted _ -> Nothing+  EventChangeMerged ChangeMerged {..} -> Just changeMergedPatchSet+  EventChangeRestored ChangeRestored {..} -> Just changeRestoredPatchSet+  EventCommentAdded CommentAdded {..} -> Just commentAddedPatchSet+  EventDroppedOutput -> Nothing+  EventHashtagsChanged _ -> Nothing+  EventProjectCreated _ -> Nothing+  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedPatchSet+  EventRefUpdated _ -> Nothing+  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedPatchSet+  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedPatchSet+  EventTopicChanged _ -> Nothing+  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedPatchSet+  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedPatchSet+  EventVoteDeleted VoteDeleted {..} -> Just voteDeletedPatchSet++-- | Get the associated author+getUser :: Event -> Maybe User+getUser event = case event of+  EventAssigneeChanged AssigneeChanged {..} -> Just assigneeChangedChanger+  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedAbandoner+  EventChangeDeleted ChangeDeleted {..} -> Just changeDeletedDeleter+  EventChangeMerged ChangeMerged {..} -> Just changeMergedSubmitter+  EventChangeRestored ChangeRestored {..} -> Just changeRestoredRestorer+  EventCommentAdded CommentAdded {..} -> Just commentAddedAuthor+  EventDroppedOutput -> Nothing+  EventHashtagsChanged HashtagsChanged {..} -> Just hashtagsChangedEditor+  EventProjectCreated _ -> Nothing+  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedUploader+  EventRefUpdated _ -> Nothing+  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedAdder+  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedRemover+  EventTopicChanged TopicChanged {..} -> Just topicChangedChanger+  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedChanger+  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedChanger+  EventVoteDeleted VoteDeleted {..} -> Just voteDeletedRemover++-- | Get the creation timestamp+getCreatedOn :: Event -> Maybe Int64+getCreatedOn event = case event of+  EventAssigneeChanged AssigneeChanged {..} -> Just assigneeChangedEventCreatedOn+  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedEventCreatedOn+  EventChangeDeleted _ -> Nothing+  EventChangeMerged ChangeMerged {..} -> Just changeMergedEventCreatedOn+  EventChangeRestored ChangeRestored {..} -> Just changeRestoredEventCreatedOn+  EventCommentAdded CommentAdded {..} -> Just commentAddedEventCreatedOn+  EventDroppedOutput -> Nothing+  EventHashtagsChanged HashtagsChanged {..} -> Just hashtagsChangedEventCreatedOn+  EventProjectCreated ProjectCreated {..} -> Just projectCreatedEventCreatedOn+  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedEventCreatedOn+  EventRefUpdated RefUpdated {..} -> Just refUpdatedEventCreatedOn+  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedEventCreatedOn+  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedEventCreatedOn+  EventTopicChanged TopicChanged {..} -> Just topicChangedEventCreatedOn+  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedEventCreatedOn+  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedChangedEventCreatedOn+  EventVoteDeleted _ -> Nothing++-- | Get the 'EventType' back from an 'Event'+getEventType :: Event -> EventType+getEventType event = case event of+  EventAssigneeChanged _ -> AssigneeChangedEvent+  EventChangeAbandoned _ -> ChangeAbandonedEvent+  EventChangeDeleted _ -> ChangeDeletedEvent+  EventChangeMerged _ -> ChangeMergedEvent+  EventChangeRestored _ -> ChangeRestoredEvent+  EventCommentAdded _ -> CommentAddedEvent+  EventDroppedOutput -> DroppedOutputEvent+  EventHashtagsChanged _ -> HashtagsChangedEvent+  EventProjectCreated _ -> ProjectCreatedEvent+  EventPatchsetCreated _ -> PatchsetCreatedEvent+  EventRefUpdated _ -> RefUpdatedEvent+  EventReviewerAdded _ -> ReviewerAddedEvent+  EventReviewerDeleted _ -> ReviewerDeletedEvent+  EventTopicChanged _ -> TopicChangedEvent+  EventWorkInProgressStateChanged _ -> WorkInProgressStateChangedEvent+  EventPrivateStateChanged _ -> PrivateStateChangedEvent+  EventVoteDeleted _ -> VoteDeletedEvent++-- | Get the text representation suitable for event subscription.+eventName :: EventType -> Text+eventName eventType = case eventType of+  AssigneeChangedEvent -> "assignee-changed"+  ChangeAbandonedEvent -> "change-abandoned"+  ChangeDeletedEvent -> "change-deleted"+  ChangeMergedEvent -> "change-merged"+  ChangeRestoredEvent -> "change-restored"+  CommentAddedEvent -> "comment-added"+  DroppedOutputEvent -> "dropped-output"+  HashtagsChangedEvent -> "hashtags-changed"+  ProjectCreatedEvent -> "project-created"+  PatchsetCreatedEvent -> "patchset-created"+  RefUpdatedEvent -> "ref-updated"+  ReviewerAddedEvent -> "reviewer-added"+  ReviewerDeletedEvent -> "reviewer-deleted"+  TopicChangedEvent -> "topic-changed"+  WorkInProgressStateChangedEvent -> "wip-state-changed"+  PrivateStateChangedEvent -> "private-state-changed"+  VoteDeletedEvent -> "vote-deleted"
+ src/Gerrit/Data/Project.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}++module Gerrit.Data.Project+  ( GerritProjectInfo (..),+    GerritProjectQuery (..),+    projectQS,+    GerritProjectsMessage,+  )+where++import Data.Aeson+import Data.Aeson.Casing (aesonPrefix, snakeCase)+import Data.Map+import Data.Text (Text, intercalate, pack)+import GHC.Generics (Generic)++data GerritProjectQuery = Regexp Text | Prefix Text++queryText :: GerritProjectQuery -> Text+queryText (Regexp re) = "r=" <> re+queryText (Prefix prefix) = "p=" <> prefix++-- >>> projectQS 10 (Regexp "test/.*|rpms/.*") Nothing+-- "r:test/.*|rpms/.*&n=10"+projectQS :: Int -> GerritProjectQuery -> Maybe Int -> Text+projectQS count query startM =+  intercalate "&" [qtString, countString] <> startString+  where+    qtString = queryText query+    countString = "n=" <> pack (show count)+    startString = maybe mempty (\s -> "&S=" <> pack (show s)) startM++newtype GerritProjectInfo = GerritProjectInfo+  {gerritprojectinfoId :: Text}+  deriving (Eq, Show, Generic)++type GerritProjectsMessage = Map Text GerritProjectInfo++instance FromJSON GerritProjectInfo where+  parseJSON = genericParseJSON $ aesonPrefix snakeCase
+ src/Gerrit/Data/Review.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE OverloadedStrings #-}++-- | This module contains the gerrit data type+module Gerrit.Data.Review+  ( GerritVersion (..),+    GerritLabel (..),+    GerritReviewAccount (..),+    GerritLabelVote (..),+    ReviewResult (..),+    ReviewInput (..),+  )+where++import Data.Aeson+import Data.Char (isUpper, toLower)+import qualified Data.Map as M+import Data.Text (Text)+import GHC.Generics (Generic)++newtype GerritVersion = GerritVersion Text+  deriving (Show, Generic)+  deriving anyclass (FromJSON)++data GerritLabelVote = REJECTED | APPROVED | DISLIKED | RECOMMENDED+  deriving (Eq, Show, Ord, Generic)++-- We use a custom parseJSON to decode Label Vote as lowercase+instance FromJSON GerritLabelVote where+  parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = map toLower}++instance FromJSONKey GerritLabelVote where+  fromJSONKey = genericFromJSONKey defaultJSONKeyOptions {keyModifier = map toLower}++-- | Modify record attribute to match json schema+-- Remove the prefix and use snakecase+customParseJSON :: String -> Options+customParseJSON prefix = defaultOptions {fieldLabelModifier = recordToJson}+  where+    recordToJson = updateCase . drop (length prefix)+    updateCase [] = []+    updateCase (x : xs) = toLower x : updateCase' xs+    updateCase' [] = []+    updateCase' (x : xs)+      | isUpper x = '_' : toLower x : updateCase' xs+      | otherwise = x : updateCase' xs++-- https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html+data ReviewResult = ReviewResult+  { rrLabels :: Maybe (M.Map Text Int),+    rrReady :: Maybe Int+  }+  deriving (Eq, Show, Ord, Generic)++instance FromJSON ReviewResult where+  parseJSON = genericParseJSON $ customParseJSON "rr"++instance ToJSON ReviewResult where+  toJSON = genericToJSON $ customParseJSON "rr"++data ReviewInput = ReviewInput+  { riMessage :: Maybe Text,+    riLabels :: Maybe (M.Map Text Int)+  }+  deriving (Eq, Show, Ord, Generic)++instance FromJSON ReviewInput where+  parseJSON = genericParseJSON $ customParseJSON "ri"++instance ToJSON ReviewInput where+  toJSON = genericToJSON $ (customParseJSON "ri") {omitNothingFields = True}++newtype GerritReviewAccount = GerritReviewAccount+  { unused_account_id :: Int+  }+  deriving (Show, Generic)++-- We use a cusom parseJSON to decode `_account_id` as `account_id`+instance FromJSON GerritReviewAccount where+  parseJSON = genericParseJSON defaultOptions++newtype GerritLabel+  = GerritLabel (M.Map GerritLabelVote GerritReviewAccount)+  deriving (Show, Generic)+  deriving anyclass (FromJSON)
− src/Gerrit/Event.hs
@@ -1,448 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}---- | Gerrit stream event data type-module Gerrit.Event-  ( -- * Main event data types-    EventType (..),-    Event (..),--    -- * Common data types-    User (..),-    PatchSet (..),-    Change (..),-    Ref (..),--    -- * Individual event data types-    AssigneeChanged (..),-    ChangeAbandoned (..),-    ChangeDeleted (..),-    ChangeMerged (..),-    ChangeRestored (..),-    CommentAdded (..),-    HashtagsChanged (..),-    ProjectCreated (..),-    PatchsetCreated (..),-    RefUpdated (..),-    ReviewerAdded (..),-    ReviewerDeleted (..),-    TopicChanged (..),-    WorkInProgressStateChanged (..),-    PrivateStateChanged (..),-    VoteDeleted (..),--    -- * Convenient functions-    getChange,-    getUser,-    getEventType,-    eventName,-  )-where--import Control.Monad (mzero)-import Data.Aeson-  ( FromJSON (..),-    Options (fieldLabelModifier),-    Value (Object, String),-    defaultOptions,-    genericParseJSON,-    (.:),-  )-import Data.Char (toLower)-import Data.Text (Text)-import qualified Data.Text as Text-import GHC.Generics (Generic)--aesonOption :: Text -> Options-aesonOption prefix = defaultOptions {fieldLabelModifier = recordToJson}-  where-    recordToJson = updateCase . drop (Text.length prefix)-    updateCase [] = []-    updateCase (x : xs) = toLower x : xs------------------------------------------------------------------------------------ Shared types---------------------------------------------------------------------------------data User = User-  { userName :: Maybe Text,-    userEmail :: Maybe Text,-    userUsername :: Maybe Text-  }-  deriving (Show, Eq, Generic)--instance FromJSON User where-  parseJSON = genericParseJSON $ aesonOption "user"--data PatchSet = PatchSet-  { patchSetNumber :: Int,-    patchSetRef :: Text,-    patchSetUploader :: User,-    patchsetAuthor :: User-  }-  deriving (Show, Eq, Generic)--instance FromJSON PatchSet where-  parseJSON = genericParseJSON $ aesonOption "patchSet"--data Change = Change-  { changeProject :: Text,-    changeBranch :: Text,-    changeSubject :: Text,-    changeUrl :: Text,-    changeOwner :: User-  }-  deriving (Show, Eq, Generic)--instance FromJSON Change where-  parseJSON = genericParseJSON $ aesonOption "change"--data Ref = Ref-  { refOldRev :: Text,-    refNewRev :: Text,-    refRefName :: Text,-    refProject :: Text-  }-  deriving (Show, Eq, Generic)--instance FromJSON Ref where-  parseJSON = genericParseJSON $ aesonOption "ref"------------------------------------------------------------------------------------ Individual Event data types---------------------------------------------------------------------------------data AssigneeChanged = AssigneeChanged-  { assigneeChangedChange :: Change,-    assigneeChangedChanger :: User-  }-  deriving (Show, Eq, Generic)--data ChangeAbandoned = ChangeAbandoned-  { changeAbandonedProject :: Text,-    changeAbandonedAbandoner :: User,-    changeAbandonedChange :: Change,-    changeAbandonedPatchSet :: PatchSet-  }-  deriving (Show, Eq, Generic)--data ChangeDeleted = ChangeDeleted-  { changeDeletedChange :: Change,-    changeDeletedDeleter :: User-  }-  deriving (Show, Eq, Generic)--data ChangeMerged = ChangeMerged-  { changeMergedProject :: Text,-    changeMergedSubmitter :: User,-    changeMergedChange :: Change,-    changeMergedPatchSet :: PatchSet-  }-  deriving (Show, Eq, Generic)--data ChangeRestored = ChangeRestored-  { changeRestoredChange :: Change,-    changeRestoredPatchSet :: PatchSet,-    changeRestoredRestorer :: User,-    changeRestoredReason :: Text-  }-  deriving (Show, Eq, Generic)--data CommentAdded = CommentAdded-  { commentAddedChange :: Change,-    commentAddedPatchSet :: PatchSet,-    commentAddedAuthor :: User-  }-  deriving (Show, Eq, Generic)--data HashtagsChanged = HashtagsChanged-  { hashtagsChangedChange :: Change,-    hashtagsChangedEditor :: User-  }-  deriving (Show, Eq, Generic)--data ProjectCreated = ProjectCreated-  { projectCreatedProjectName :: Text,-    projectCreatedHeadName :: Text-  }-  deriving (Show, Eq, Generic)--data PatchsetCreated = PatchsetCreated-  { patchsetCreatedUploader :: User,-    patchsetCreatedChange :: Change,-    patchsetCreatedPatchSet :: PatchSet-  }-  deriving (Show, Eq, Generic)--data RefUpdated = RefUpdated-  { refUpdatedSubmitter :: User,-    refUpdatedRefUpdate :: Ref-  }-  deriving (Show, Eq, Generic)--data ReviewerAdded = ReviewerAdded-  { reviewerAddedChange :: Change,-    reviewerAddedPatchSet :: PatchSet,-    reviewerAddedAdder :: User-  }-  deriving (Show, Eq, Generic)--data ReviewerDeleted = ReviewerDeleted-  { reviewerDeletedChange :: Change,-    reviewerDeletedPatchSet :: PatchSet,-    reviewerDeletedRemover :: User-  }-  deriving (Show, Eq, Generic)--data TopicChanged = TopicChanged-  { topicChangedChange :: Change,-    topicChangedChanger :: User,-    topicChangedOldTopic :: Text-  }-  deriving (Show, Eq, Generic)--data WorkInProgressStateChanged = WorkInProgressStateChanged-  { workInProgressStateChangedChange :: Change,-    workInProgressStateChangedPatchSet :: PatchSet,-    workInProgressStateChangedChanger :: User-  }-  deriving (Show, Eq, Generic)--data PrivateStateChanged = PrivateStateChanged-  { privateStateChangedChange :: Change,-    privateStateChangedPatchSet :: PatchSet,-    privateStateChangedChanger :: User-  }-  deriving (Show, Eq, Generic)--data VoteDeleted = VoteDeleted-  { voteDeletedChange :: Change,-    voteDeletedPatchSet :: PatchSet,-    voteDeletedRemover :: User-  }-  deriving (Show, Eq, Generic)------------------------------------------------------------------------------------ Events FromJSON instances---------------------------------------------------------------------------------instance FromJSON AssigneeChanged where-  parseJSON = genericParseJSON $ aesonOption "assigneeChanged"--instance FromJSON ChangeAbandoned where-  parseJSON = genericParseJSON $ aesonOption "changeAbandoned"--instance FromJSON ChangeDeleted where-  parseJSON = genericParseJSON $ aesonOption "changeDeleted"--instance FromJSON ChangeMerged where-  parseJSON = genericParseJSON $ aesonOption "changeMerged"--instance FromJSON ChangeRestored where-  parseJSON = genericParseJSON $ aesonOption "changeRestored"--instance FromJSON CommentAdded where-  parseJSON = genericParseJSON $ aesonOption "commentAdded"--instance FromJSON HashtagsChanged where-  parseJSON = genericParseJSON $ aesonOption "hashtagsChanged"--instance FromJSON ProjectCreated where-  parseJSON = genericParseJSON $ aesonOption "projectCreated"--instance FromJSON PatchsetCreated where-  parseJSON = genericParseJSON $ aesonOption "patchsetCreated"--instance FromJSON RefUpdated where-  parseJSON = genericParseJSON $ aesonOption "refUpdated"--instance FromJSON ReviewerAdded where-  parseJSON = genericParseJSON $ aesonOption "reviewerAdded"--instance FromJSON ReviewerDeleted where-  parseJSON = genericParseJSON $ aesonOption "reviewerDeleted"--instance FromJSON TopicChanged where-  parseJSON = genericParseJSON $ aesonOption "topicChanged"--instance FromJSON WorkInProgressStateChanged where-  parseJSON = genericParseJSON $ aesonOption "workInProgressStateChanged"--instance FromJSON PrivateStateChanged where-  parseJSON = genericParseJSON $ aesonOption "privateStateChanged"--instance FromJSON VoteDeleted where-  parseJSON = genericParseJSON $ aesonOption "voteDeleted"---- | The event "type" data type-data EventType-  = AssigneeChangedEvent-  | ChangeAbandonedEvent-  | ChangeDeletedEvent-  | ChangeMergedEvent-  | ChangeRestoredEvent-  | CommentAddedEvent-  | DroppedOutputEvent-  | HashtagsChangedEvent-  | ProjectCreatedEvent-  | PatchsetCreatedEvent-  | RefUpdatedEvent-  | ReviewerAddedEvent-  | ReviewerDeletedEvent-  | TopicChangedEvent-  | WorkInProgressStateChangedEvent-  | PrivateStateChangedEvent-  | VoteDeletedEvent-  deriving (Show, Eq)--instance FromJSON EventType where-  parseJSON (String v) = case v of-    "assignee-changed" -> pure AssigneeChangedEvent-    "change-abandoned" -> pure ChangeAbandonedEvent-    "change-deleted" -> pure ChangeDeletedEvent-    "change-merged" -> pure ChangeMergedEvent-    "change-restored" -> pure ChangeRestoredEvent-    "comment-added" -> pure CommentAddedEvent-    "dropped-output" -> pure DroppedOutputEvent-    "hashtags-changed" -> pure HashtagsChangedEvent-    "project-created" -> pure ProjectCreatedEvent-    "patchset-created" -> pure PatchsetCreatedEvent-    "ref-updated" -> pure RefUpdatedEvent-    "reviewer-added" -> pure ReviewerAddedEvent-    "reviewer-deleted" -> pure ReviewerDeletedEvent-    "topic-changed" -> pure TopicChangedEvent-    "wip-state-changed" -> pure WorkInProgressStateChangedEvent-    "private-state-changed" -> pure PrivateStateChangedEvent-    "vote-deleted" -> pure VoteDeletedEvent-    _ -> mzero-  parseJSON _ = mzero---- | The main event union data type-data Event-  = EventAssigneeChanged AssigneeChanged-  | EventChangeAbandoned ChangeAbandoned-  | EventChangeDeleted ChangeDeleted-  | EventChangeMerged ChangeMerged-  | EventChangeRestored ChangeRestored-  | EventCommentAdded CommentAdded-  | EventDroppedOutput-  | EventHashtagsChanged HashtagsChanged-  | EventProjectCreated ProjectCreated-  | EventPatchsetCreated PatchsetCreated-  | EventRefUpdated RefUpdated-  | EventReviewerAdded ReviewerAdded-  | EventReviewerDeleted ReviewerDeleted-  | EventTopicChanged TopicChanged-  | EventWorkInProgressStateChanged WorkInProgressStateChanged-  | EventPrivateStateChanged PrivateStateChanged-  | EventVoteDeleted VoteDeleted-  deriving (Show, Eq)--instance FromJSON Event where-  parseJSON o@(Object v) = do-    eType <- v .: "type"-    case eType of-      AssigneeChangedEvent -> EventAssigneeChanged <$> parseJSON o-      ChangeAbandonedEvent -> EventChangeAbandoned <$> parseJSON o-      ChangeDeletedEvent -> EventChangeDeleted <$> parseJSON o-      ChangeMergedEvent -> EventChangeMerged <$> parseJSON o-      ChangeRestoredEvent -> EventChangeRestored <$> parseJSON o-      CommentAddedEvent -> EventCommentAdded <$> parseJSON o-      DroppedOutputEvent -> pure EventDroppedOutput-      HashtagsChangedEvent -> EventHashtagsChanged <$> parseJSON o-      ProjectCreatedEvent -> EventProjectCreated <$> parseJSON o-      PatchsetCreatedEvent -> EventPatchsetCreated <$> parseJSON o-      RefUpdatedEvent -> EventRefUpdated <$> parseJSON o-      ReviewerAddedEvent -> EventReviewerAdded <$> parseJSON o-      ReviewerDeletedEvent -> EventReviewerDeleted <$> parseJSON o-      TopicChangedEvent -> EventTopicChanged <$> parseJSON o-      WorkInProgressStateChangedEvent -> EventWorkInProgressStateChanged <$> parseJSON o-      PrivateStateChangedEvent -> EventPrivateStateChanged <$> parseJSON o-      VoteDeletedEvent -> EventVoteDeleted <$> parseJSON o-  parseJSON _ = mzero------------------------------------------------------------------------------------ Utilities function------------------------------------------------------------------------------------ | Get the associated change-getChange :: Event -> Maybe Change-getChange event = case event of-  EventAssigneeChanged AssigneeChanged {..} -> Just assigneeChangedChange-  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedChange-  EventChangeDeleted ChangeDeleted {..} -> Just changeDeletedChange-  EventChangeMerged ChangeMerged {..} -> Just changeMergedChange-  EventChangeRestored ChangeRestored {..} -> Just changeRestoredChange-  EventCommentAdded CommentAdded {..} -> Just commentAddedChange-  EventDroppedOutput -> Nothing-  EventHashtagsChanged HashtagsChanged {..} -> Just hashtagsChangedChange-  EventProjectCreated _ -> Nothing-  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedChange-  EventRefUpdated _ -> Nothing-  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedChange-  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedChange-  EventTopicChanged TopicChanged {..} -> Just topicChangedChange-  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedChange-  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedChange-  EventVoteDeleted VoteDeleted {..} -> Just voteDeletedChange---- | Get the associated author-getUser :: Event -> Maybe User-getUser event = case event of-  EventAssigneeChanged AssigneeChanged {..} -> Just assigneeChangedChanger-  EventChangeAbandoned ChangeAbandoned {..} -> Just changeAbandonedAbandoner-  EventChangeDeleted ChangeDeleted {..} -> Just changeDeletedDeleter-  EventChangeMerged ChangeMerged {..} -> Just changeMergedSubmitter-  EventChangeRestored ChangeRestored {..} -> Just changeRestoredRestorer-  EventCommentAdded CommentAdded {..} -> Just commentAddedAuthor-  EventDroppedOutput -> Nothing-  EventHashtagsChanged HashtagsChanged {..} -> Just hashtagsChangedEditor-  EventProjectCreated _ -> Nothing-  EventPatchsetCreated PatchsetCreated {..} -> Just patchsetCreatedUploader-  EventRefUpdated _ -> Nothing-  EventReviewerAdded ReviewerAdded {..} -> Just reviewerAddedAdder-  EventReviewerDeleted ReviewerDeleted {..} -> Just reviewerDeletedRemover-  EventTopicChanged TopicChanged {..} -> Just topicChangedChanger-  EventWorkInProgressStateChanged WorkInProgressStateChanged {..} -> Just workInProgressStateChangedChanger-  EventPrivateStateChanged PrivateStateChanged {..} -> Just privateStateChangedChanger-  EventVoteDeleted VoteDeleted {..} -> Just voteDeletedRemover---- | Get the 'EventType' back from an 'Event'-getEventType :: Event -> EventType-getEventType event = case event of-  EventAssigneeChanged _ -> AssigneeChangedEvent-  EventChangeAbandoned _ -> ChangeAbandonedEvent-  EventChangeDeleted _ -> ChangeDeletedEvent-  EventChangeMerged _ -> ChangeMergedEvent-  EventChangeRestored _ -> ChangeRestoredEvent-  EventCommentAdded _ -> CommentAddedEvent-  EventDroppedOutput -> DroppedOutputEvent-  EventHashtagsChanged _ -> HashtagsChangedEvent-  EventProjectCreated _ -> ProjectCreatedEvent-  EventPatchsetCreated _ -> PatchsetCreatedEvent-  EventRefUpdated _ -> RefUpdatedEvent-  EventReviewerAdded _ -> ReviewerAddedEvent-  EventReviewerDeleted _ -> ReviewerDeletedEvent-  EventTopicChanged _ -> TopicChangedEvent-  EventWorkInProgressStateChanged _ -> WorkInProgressStateChangedEvent-  EventPrivateStateChanged _ -> PrivateStateChangedEvent-  EventVoteDeleted _ -> VoteDeletedEvent---- | Get the text representation suitable for event subscription.-eventName :: EventType -> Text-eventName eventType = case eventType of-  AssigneeChangedEvent -> "assignee-changed"-  ChangeAbandonedEvent -> "change-abandoned"-  ChangeDeletedEvent -> "change-deleted"-  ChangeMergedEvent -> "change-merged"-  ChangeRestoredEvent -> "change-restored"-  CommentAddedEvent -> "comment-added"-  DroppedOutputEvent -> "dropped-output"-  HashtagsChangedEvent -> "hashtags-changed"-  ProjectCreatedEvent -> "project-created"-  PatchsetCreatedEvent -> "patchset-created"-  RefUpdatedEvent -> "ref-updated"-  ReviewerAddedEvent -> "reviewer-added"-  ReviewerDeletedEvent -> "reviewer-deleted"-  TopicChangedEvent -> "topic-changed"-  WorkInProgressStateChangedEvent -> "wip-state-changed"-  PrivateStateChangedEvent -> "private-state-changed"-  VoteDeletedEvent -> "vote-deleted"
test/Spec.hs view
@@ -8,8 +8,9 @@ import qualified Data.ByteString.Lazy as BSL import Data.Maybe (fromJust) import Gerrit-import Gerrit.Data as Gerrit-import qualified Gerrit.Event as Event+import Gerrit.Data.Change+import qualified Gerrit.Data.Event as Event+import Gerrit.Data.Review import System.Directory (listDirectory) import Test.Tasty import Test.Tasty.HUnit@@ -17,10 +18,10 @@ main :: IO () main =   do-    files <- listDirectory "./test/data"-    dataFiles <- traverse (BSL.readFile . ("./test/data/" <>)) files+    files' <- listDirectory "./test/data"+    dataFiles <- traverse (BSL.readFile . ("./test/data/" <>)) files'     withClient "http://example.com/r" Nothing $ \client ->-      defaultMain (tests (zip files dataFiles) client)+      defaultMain (tests (zip files' dataFiles) client)  tests :: [(FilePath, ByteString)] -> GerritClient -> TestTree tests dataFiles client = testGroup "Tests" $ [unitTests] <> encodingTests dataFiles client@@ -30,8 +31,15 @@   testGroup     "unitTests"     [ testCase "queryString status" $-        assertEqual "Query string is valid" "status:new" (queryText (Status NEW))+        assertEqual "Query string is valid" "status:new" (queryText (Status NEW)),+      testCase "changeQS w/o start" $+        assertEqual "Change query is valid" expected1 (changeQS 10 [Project "gerrit-haskell"] Nothing),+      testCase "changeQS with start" $+        assertEqual "Change query is valid" expected2 (changeQS 10 [Project "gerrit-haskell"] (Just 100))     ]+  where+    expected1 = "q=project:gerrit-haskell&n=10&o=MESSAGES&o=DETAILED_ACCOUNTS&o=DETAILED_LABELS&o=CURRENT_REVISION&o=CURRENT_FILES&o=CURRENT_COMMIT"+    expected2 = "q=project:gerrit-haskell&n=10&o=MESSAGES&o=DETAILED_ACCOUNTS&o=DETAILED_LABELS&o=CURRENT_REVISION&o=CURRENT_FILES&o=CURRENT_COMMIT&start=100"  encodingTests :: [(FilePath, ByteString)] -> GerritClient -> [TestTree] encodingTests dataFiles client =@@ -74,7 +82,7 @@   ]   where     prettyEncode obj = encode obj <> "\n"-    reviewInput = Gerrit.ReviewInput (Just "Thanks!") Nothing+    reviewInput = ReviewInput (Just "Thanks!") Nothing     getRaw :: FilePath -> ByteString     getRaw fp = fromJust $ lookup fp dataFiles     isReviewResult :: Maybe ReviewResult -> Bool@@ -104,7 +112,7 @@       Just _ -> True       Nothing -> False     testIsApproved = case decode (getRaw "GerritChange.json") of-      Just change -> hasLabel "Code-Review" 1 change+      Just change -> hasLabel "Code-Review" 2 change       Nothing -> False     testGetUrl = case decode (getRaw "GerritChange.json") of       Just change -> changeUrl client change
test/data/GerritChange.json view
@@ -5,33 +5,127 @@   "hashtags": [],   "change_id": "Ic6add806c49bccaf6e15d4ef1c62522d56eb9384",   "subject": "Initial summon",-  "status": "NEW",+  "status": "MERGED",   "created": "2020-07-15 19:39:55.000000000",-  "updated": "2020-07-15 23:01:14.000000000",-  "submit_type": "MERGE_IF_NECESSARY",-  "mergeable": true,+  "updated": "2020-07-16 18:08:59.000000000",+  "submitted": "2020-07-16 18:08:59.000000000",+  "submitter": {+    "_account_id": 458,+    "name": "Zuul CI",+    "email": "zuul@softwarefactory-project.io",+    "username": "zuul",+    "avatars": [+      {+        "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+        "height": 32+      },+      {+        "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+        "height": 56+      },+      {+        "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+        "height": 100+      },+      {+        "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+        "height": 120+      }+    ]+  },   "insertions": 295,   "deletions": 0,+  "total_comment_count": 0,   "unresolved_comment_count": 0,+  "has_review_started": true,+  "submission_id": "18839-1594922939722-7c5a83e2",   "_number": 18839,   "owner": {-    "_account_id": 7+    "_account_id": 7,+    "name": "Tristan de Cacqueray",+    "email": "tdecacqu@redhat.com",+    "username": "TristanCacqueray",+    "avatars": [+      {+        "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+        "height": 32+      },+      {+        "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+        "height": 56+      },+      {+        "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+        "height": 100+      },+      {+        "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+        "height": 120+      }+    ]   },   "labels": {     "Code-Review": {       "all": [         {-          "value": 1,+          "value": 0,+          "_account_id": 458,+          "name": "Zuul CI",+          "email": "zuul@softwarefactory-project.io",+          "username": "zuul",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]+        },+        {+          "value": 2,+          "date": "2020-07-16 18:08:48.000000000",           "permitted_voting_range": {-            "min": -1,-            "max": 1+            "min": 2,+            "max": 2           },-          "_account_id": 458+          "_account_id": 7,+          "name": "Tristan de Cacqueray",+          "email": "tdecacqu@redhat.com",+          "username": "TristanCacqueray",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]         }       ],       "values": {         "-2": "Do not submit",-        "-1": "I would prefer that you didn't submit this",+        "-1": "I would prefer that you didn\u0027t submit this",         " 0": "No score",         "+1": "Looks good to me, but someone else must approve",         "+2": "Looks good to me (core reviewer)"@@ -41,28 +135,683 @@     "Verified": {       "all": [         {-          "value": 1,-          "date": "2020-07-17 13:02:15.000000000",+          "value": 2,+          "date": "2020-07-16 18:08:59.000000000",+          "post_submit": true,           "permitted_voting_range": {-            "min": -2,+            "min": 2,             "max": 2           },-          "_account_id": 458+          "_account_id": 458,+          "name": "Zuul CI",+          "email": "zuul@softwarefactory-project.io",+          "username": "zuul",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]         },         {-          "_account_id": 42+          "value": 0,+          "_account_id": 7,+          "name": "Tristan de Cacqueray",+          "email": "tdecacqu@redhat.com",+          "username": "TristanCacqueray",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]         }       ],       "values": {         "-2": "Fails",-        "-1": "Doesn't seem to work",+        "-1": "Doesn\u0027t seem to work",         " 0": "No score",         "+1": "Works for me",         "+2": "Verified"       },       "default_value": 0+    },+    "Workflow": {+      "all": [+        {+          "value": 0,+          "_account_id": 458,+          "name": "Zuul CI",+          "email": "zuul@softwarefactory-project.io",+          "username": "zuul",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]+        },+        {+          "value": 1,+          "date": "2020-07-16 18:08:48.000000000",+          "permitted_voting_range": {+            "min": 1,+            "max": 1+          },+          "_account_id": 7,+          "name": "Tristan de Cacqueray",+          "email": "tdecacqu@redhat.com",+          "username": "TristanCacqueray",+          "avatars": [+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+              "height": 32+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+              "height": 56+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+              "height": 100+            },+            {+              "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+              "height": 120+            }+          ]+        }+      ],+      "values": {+        "-1": "Work in progress",+        " 0": "Ready for reviews",+        "+1": "Approved"+      },+      "default_value": 0     }   },+  "removable_reviewers": [],+  "reviewers": {+    "REVIEWER": [+      {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      }+    ]+  },+  "pending_reviewers": {},+  "messages": [+    {+      "id": "ee2e77b6628d5a007029fab6ed7a6f6132c031c9",+      "tag": "autogenerated:gerrit:newPatchSet",+      "author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-15 19:39:55.000000000",+      "message": "Uploaded patch set 1.",+      "_revision_number": 1+    },+    {+      "id": "3e1eb0ecb29947dda8ef79bbe67c193e9465db7a",+      "tag": "autogenerated:gerrit:newPatchSet",+      "author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-15 21:03:21.000000000",+      "message": "Uploaded patch set 2.",+      "_revision_number": 2+    },+    {+      "id": "ba03042aa7252f4ff133d31f66b75c60f80c067f",+      "tag": "autogenerated:gerrit:newPatchSet",+      "author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-15 23:01:02.000000000",+      "message": "Uploaded patch set 3.",+      "_revision_number": 3+    },+    {+      "id": "dc3ddbc6022f57505b3924d4c42df19ea0d2d527",+      "author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-15 23:01:13.000000000",+      "message": "Patch Set 3:\n\nStarting check jobs. Status at",+      "_revision_number": 3+    },+    {+      "id": "f053926475b8fd9da58d55ae47d8f7eef3a3a727",+      "author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-15 23:01:14.000000000",+      "message": "Patch Set 3: Verified+1\n\nBuild succeeded.\n\n- noop https://softwarefactory-project.io/zuul/t/local/build/c6511d87e5f644e7b566b73741600392 : SUCCESS in 0s",+      "_revision_number": 3+    },+    {+      "id": "9d688628218ba8972d8ecf51669bec9c83d3a18f",+      "author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-16 18:08:48.000000000",+      "message": "Patch Set 3: Code-Review+2 Workflow+1",+      "_revision_number": 3+    },+    {+      "id": "f2dcf8b15417b0057b2e7384c51d194cce7a0491",+      "tag": "autogenerated:gerrit:merged",+      "author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-16 18:08:59.000000000",+      "message": "Change has been successfully merged by Zuul CI",+      "_revision_number": 3+    },+    {+      "id": "10a3e0eeee03627b1d57a9e932a479545a2f2ade",+      "author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-16 18:08:59.000000000",+      "message": "Patch Set 3: -Verified\n\nStarting gate jobs.",+      "_revision_number": 3+    },+    {+      "id": "b3d3d3639dc32a9ee80d7b65a82ab57615925d6d",+      "author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "real_author": {+        "_account_id": 458,+        "name": "Zuul CI",+        "email": "zuul@softwarefactory-project.io",+        "username": "zuul",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/72ca689f16f3f13bf36869ea655c488e.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]+      },+      "date": "2020-07-16 18:08:59.000000000",+      "message": "Patch Set 3: Verified+2\n\nBuild succeeded (gate pipeline).\n\n- noop https://softwarefactory-project.io/zuul/t/local/build/261409bf24ea4b20a8a4304bcbed343a : SUCCESS in 0s",+      "_revision_number": 3+    }+  ],   "current_revision": "155658085799464e8d198edb7c8bd62de9ec9ae6",   "revisions": {     "155658085799464e8d198edb7c8bd62de9ec9ae6": {@@ -70,7 +819,28 @@       "_number": 3,       "created": "2020-07-15 23:01:02.000000000",       "uploader": {-        "_account_id": 7+        "_account_id": 7,+        "name": "Tristan de Cacqueray",+        "email": "tdecacqu@redhat.com",+        "username": "TristanCacqueray",+        "avatars": [+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d32",+            "height": 32+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d56",+            "height": 56+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d100",+            "height": 100+          },+          {+            "url": "https://www.gravatar.com/avatar/921d0ed12095380e7b79d606bb037261.jpg?d\u003didenticon\u0026r\u003dpg\u0026s\u003d120",+            "height": 120+          }+        ]       },       "ref": "refs/changes/39/18839/3",       "fetch": {@@ -78,7 +848,80 @@           "url": "https://softwarefactory-project.io/r/software-factory/gerrit-haskell",           "ref": "refs/changes/39/18839/3"         }+      },+      "commit": {+        "parents": [+          {+            "commit": "fda786eda9e53208ede9b571174b85e4be39a44d",+            "subject": "ManageSF commit"+          }+        ],+        "author": {+          "name": "Tristan Cacqueray",+          "email": "tdecacqu@redhat.com",+          "date": "2020-07-15 19:38:27.000000000",+          "tz": 0+        },+        "committer": {+          "name": "Tristan Cacqueray",+          "email": "tdecacqu@redhat.com",+          "date": "2020-07-15 23:00:30.000000000",+          "tz": 0+        },+        "subject": "Initial summon",+        "message": "Initial summon\n\nChange-Id: Ic6add806c49bccaf6e15d4ef1c62522d56eb9384\n"+      },+      "files": {+        ".zuul.yaml": {+          "status": "A",+          "lines_inserted": 5,+          "size_delta": 71,+          "size": 71+        },+        "LICENSE": {+          "status": "A",+          "lines_inserted": 201,+          "size_delta": 11357,+          "size": 11357+        },+        "README.md": {+          "status": "A",+          "lines_inserted": 3,+          "size_delta": 34,+          "size": 34+        },+        "app/Main.hs": {+          "status": "A",+          "lines_inserted": 6,+          "size_delta": 82,+          "size": 82+        },+        "gerrit-haskell.cabal": {+          "status": "A",+          "lines_inserted": 61,+          "size_delta": 1863,+          "size": 1863+        },+        "src/Gerrit.hs": {+          "status": "A",+          "lines_inserted": 13,+          "size_delta": 279,+          "size": 279+        },+        "stack.yaml": {+          "status": "A",+          "lines_inserted": 1,+          "size_delta": 19,+          "size": 19+        },+        "test/Spec.hs": {+          "status": "A",+          "lines_inserted": 5,+          "size_delta": 101,+          "size": 101+        }       }     }-  }+  },+  "requirements": [] }