packages feed

bugzilla-redhat 0.3.3 → 1.0.0

raw patch · 15 files changed

+1382/−1438 lines, 15 filesdep ~aesondep ~bytestringdep ~resourcetPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: aeson, bytestring, resourcet, text, time

API changes (from Hackage documentation)

- Web.Bugzilla.RedHat: AnonymousSession :: BugzillaContext -> BugzillaSession
- Web.Bugzilla.RedHat: ApikeySession :: BugzillaContext -> BugzillaApikey -> BugzillaSession
- Web.Bugzilla.RedHat: Attachment :: !AttachmentId -> !BugId -> Text -> Text -> UserEmail -> Bool -> Bool -> Bool -> [Flag] -> UTCTime -> UTCTime -> Text -> !Int -> Text -> Attachment
- Web.Bugzilla.RedHat: BoolFieldChange :: Field Bool -> Modification Bool -> Change
- Web.Bugzilla.RedHat: Bug :: !BugId -> Maybe [Text] -> UserEmail -> User -> [BugId] -> [UserEmail] -> [User] -> Text -> [Text] -> UTCTime -> UserEmail -> User -> [BugId] -> Maybe BugId -> Maybe [Flag] -> [Text] -> Bool -> Bool -> Bool -> Bool -> [Text] -> UTCTime -> Text -> Text -> Text -> Text -> UserEmail -> Text -> [Text] -> Text -> Text -> Text -> Text -> Text -> [Text] -> Text -> KeyMap Text -> Maybe [ExternalBug] -> Bug
- Web.Bugzilla.RedHat: BugzillaAPIError :: Int -> String -> BugzillaException
- Web.Bugzilla.RedHat: BugzillaApikey :: Text -> BugzillaApikey
- Web.Bugzilla.RedHat: BugzillaJSONParseError :: String -> BugzillaException
- Web.Bugzilla.RedHat: BugzillaToken :: Text -> BugzillaToken
- Web.Bugzilla.RedHat: BugzillaUnexpectedValue :: String -> BugzillaException
- Web.Bugzilla.RedHat: Comment :: !CommentId -> !BugId -> Maybe AttachmentId -> !Int -> Text -> UserEmail -> UTCTime -> Bool -> Comment
- Web.Bugzilla.RedHat: ExternalBug :: Text -> Int -> Text -> Text -> Text -> Int -> ExternalType -> ExternalBug
- Web.Bugzilla.RedHat: ExternalType :: Text -> Text -> Int -> Text -> Text -> ExternalType
- Web.Bugzilla.RedHat: Flag :: !FlagId -> !FlagType -> Text -> UserEmail -> Text -> UTCTime -> UTCTime -> Maybe UserEmail -> Flag
- Web.Bugzilla.RedHat: History :: !BugId -> [HistoryEvent] -> History
- Web.Bugzilla.RedHat: HistoryEvent :: EventId -> UTCTime -> UserEmail -> [Change] -> HistoryEvent
- Web.Bugzilla.RedHat: IntFieldChange :: Field Int -> Modification Int -> Change
- Web.Bugzilla.RedHat: ListFieldChange :: Field [Text] -> Modification [Text] -> Change
- Web.Bugzilla.RedHat: LoginSession :: BugzillaContext -> BugzillaToken -> BugzillaSession
- Web.Bugzilla.RedHat: Modification :: Maybe a -> Maybe a -> Maybe AttachmentId -> Modification a
- Web.Bugzilla.RedHat: TextFieldChange :: Field Text -> Modification Text -> Change
- Web.Bugzilla.RedHat: TimeFieldChange :: Field UTCTime -> Modification UTCTime -> Change
- Web.Bugzilla.RedHat: User :: !UserId -> Maybe UserEmail -> Text -> Text -> User
- Web.Bugzilla.RedHat: [AliasField] :: Field [Text]
- Web.Bugzilla.RedHat: [AssignedToField] :: Field UserEmail
- Web.Bugzilla.RedHat: [AttachmentCreatorField] :: Field UserEmail
- Web.Bugzilla.RedHat: [AttachmentDataField] :: Field Text
- Web.Bugzilla.RedHat: [AttachmentDescriptionField] :: Field Text
- Web.Bugzilla.RedHat: [AttachmentFilenameField] :: Field Text
- Web.Bugzilla.RedHat: [AttachmentIsObsoleteField] :: Field Bool
- Web.Bugzilla.RedHat: [AttachmentIsPatchField] :: Field Bool
- Web.Bugzilla.RedHat: [AttachmentIsPrivateField] :: Field Bool
- Web.Bugzilla.RedHat: [AttachmentMimetypeField] :: Field Text
- Web.Bugzilla.RedHat: [BlocksField] :: Field Int
- Web.Bugzilla.RedHat: [BugIdField] :: Field Int
- Web.Bugzilla.RedHat: [BugURLField] :: Field Text
- Web.Bugzilla.RedHat: [CcField] :: Field UserEmail
- Web.Bugzilla.RedHat: [CcListAccessibleField] :: Field Bool
- Web.Bugzilla.RedHat: [ChangedField] :: Field UTCTime
- Web.Bugzilla.RedHat: [ClassificationField] :: Field Text
- Web.Bugzilla.RedHat: [CommentCountField] :: Field Int
- Web.Bugzilla.RedHat: [CommentField] :: Field Text
- Web.Bugzilla.RedHat: [CommentIsPrivateField] :: Field Text
- Web.Bugzilla.RedHat: [CommentTagsField] :: Field Text
- Web.Bugzilla.RedHat: [CommenterField] :: Field UserEmail
- Web.Bugzilla.RedHat: [ComponentField] :: Field [Text]
- Web.Bugzilla.RedHat: [ContentField] :: Field Text
- Web.Bugzilla.RedHat: [CreationDateField] :: Field UTCTime
- Web.Bugzilla.RedHat: [CustomField] :: Text -> Field Text
- Web.Bugzilla.RedHat: [DaysElapsedField] :: Field Int
- Web.Bugzilla.RedHat: [DependsOnField] :: Field Int
- Web.Bugzilla.RedHat: [EverConfirmedField] :: Field Bool
- Web.Bugzilla.RedHat: [FlagRequesteeField] :: Field UserEmail
- Web.Bugzilla.RedHat: [FlagSetterField] :: Field UserEmail
- Web.Bugzilla.RedHat: [FlagsField] :: Field Text
- Web.Bugzilla.RedHat: [GroupField] :: Field Text
- Web.Bugzilla.RedHat: [HardwareField] :: Field Text
- Web.Bugzilla.RedHat: [KeywordsField] :: Field [Text]
- Web.Bugzilla.RedHat: [OperatingSystemField] :: Field Text
- Web.Bugzilla.RedHat: [PriorityField] :: Field Text
- Web.Bugzilla.RedHat: [ProductField] :: Field Text
- Web.Bugzilla.RedHat: [QaContactField] :: Field UserEmail
- Web.Bugzilla.RedHat: [ReporterAccessibleField] :: Field Bool
- Web.Bugzilla.RedHat: [ReporterField] :: Field UserEmail
- Web.Bugzilla.RedHat: [ResolutionField] :: Field Text
- Web.Bugzilla.RedHat: [RestrictCommentsField] :: Field Bool
- Web.Bugzilla.RedHat: [SeeAlsoField] :: Field Text
- Web.Bugzilla.RedHat: [SeverityField] :: Field Text
- Web.Bugzilla.RedHat: [StatusField] :: Field Text
- Web.Bugzilla.RedHat: [SummaryField] :: Field Text
- Web.Bugzilla.RedHat: [TagsField] :: Field Text
- Web.Bugzilla.RedHat: [TargetMilestoneField] :: Field Text
- Web.Bugzilla.RedHat: [TimeSinceAssigneeTouchedField] :: Field Int
- Web.Bugzilla.RedHat: [VersionField] :: Field Text
- Web.Bugzilla.RedHat: [VotesField] :: Field Text
- Web.Bugzilla.RedHat: [WhiteboardField] :: Field Text
- Web.Bugzilla.RedHat: [attachmentBugId] :: Attachment -> !BugId
- Web.Bugzilla.RedHat: [attachmentContentType] :: Attachment -> Text
- Web.Bugzilla.RedHat: [attachmentCreationTime] :: Attachment -> UTCTime
- Web.Bugzilla.RedHat: [attachmentCreator] :: Attachment -> UserEmail
- Web.Bugzilla.RedHat: [attachmentData] :: Attachment -> Text
- Web.Bugzilla.RedHat: [attachmentFileName] :: Attachment -> Text
- Web.Bugzilla.RedHat: [attachmentFlags] :: Attachment -> [Flag]
- Web.Bugzilla.RedHat: [attachmentId] :: Attachment -> !AttachmentId
- Web.Bugzilla.RedHat: [attachmentIsObsolete] :: Attachment -> Bool
- Web.Bugzilla.RedHat: [attachmentIsPatch] :: Attachment -> Bool
- Web.Bugzilla.RedHat: [attachmentIsPrivate] :: Attachment -> Bool
- Web.Bugzilla.RedHat: [attachmentLastChangeTime] :: Attachment -> UTCTime
- Web.Bugzilla.RedHat: [attachmentSize] :: Attachment -> !Int
- Web.Bugzilla.RedHat: [attachmentSummary] :: Attachment -> Text
- Web.Bugzilla.RedHat: [bugAlias] :: Bug -> Maybe [Text]
- Web.Bugzilla.RedHat: [bugAssignedToDetail] :: Bug -> User
- Web.Bugzilla.RedHat: [bugAssignedTo] :: Bug -> UserEmail
- Web.Bugzilla.RedHat: [bugBlocks] :: Bug -> [BugId]
- Web.Bugzilla.RedHat: [bugCcDetail] :: Bug -> [User]
- Web.Bugzilla.RedHat: [bugCc] :: Bug -> [UserEmail]
- Web.Bugzilla.RedHat: [bugClassification] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugComponent] :: Bug -> [Text]
- Web.Bugzilla.RedHat: [bugCreationTime] :: Bug -> UTCTime
- Web.Bugzilla.RedHat: [bugCreatorDetail] :: Bug -> User
- Web.Bugzilla.RedHat: [bugCreator] :: Bug -> UserEmail
- Web.Bugzilla.RedHat: [bugCustomFields] :: Bug -> KeyMap Text
- Web.Bugzilla.RedHat: [bugDependsOn] :: Bug -> [BugId]
- Web.Bugzilla.RedHat: [bugDupeOf] :: Bug -> Maybe BugId
- Web.Bugzilla.RedHat: [bugExternalBugs] :: Bug -> Maybe [ExternalBug]
- Web.Bugzilla.RedHat: [bugFlags] :: Bug -> Maybe [Flag]
- Web.Bugzilla.RedHat: [bugGroups] :: Bug -> [Text]
- Web.Bugzilla.RedHat: [bugId] :: Bug -> !BugId
- Web.Bugzilla.RedHat: [bugIsCcAccessible] :: Bug -> Bool
- Web.Bugzilla.RedHat: [bugIsConfirmed] :: Bug -> Bool
- Web.Bugzilla.RedHat: [bugIsCreatorAccessible] :: Bug -> Bool
- Web.Bugzilla.RedHat: [bugIsOpen] :: Bug -> Bool
- Web.Bugzilla.RedHat: [bugKeywords] :: Bug -> [Text]
- Web.Bugzilla.RedHat: [bugLastChangeTime] :: Bug -> UTCTime
- Web.Bugzilla.RedHat: [bugOpSys] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugPlatform] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugPriority] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugProduct] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugQaContact] :: Bug -> UserEmail
- Web.Bugzilla.RedHat: [bugResolution] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugSeeAlso] :: Bug -> [Text]
- Web.Bugzilla.RedHat: [bugSeverity] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugStatus] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugSummary] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugTargetMilestone] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugUrl] :: Bug -> Text
- Web.Bugzilla.RedHat: [bugVersion] :: Bug -> [Text]
- Web.Bugzilla.RedHat: [bugWhiteboard] :: Bug -> Text
- Web.Bugzilla.RedHat: [commentAttachmentId] :: Comment -> Maybe AttachmentId
- Web.Bugzilla.RedHat: [commentBugId] :: Comment -> !BugId
- Web.Bugzilla.RedHat: [commentCount] :: Comment -> !Int
- Web.Bugzilla.RedHat: [commentCreationTime] :: Comment -> UTCTime
- Web.Bugzilla.RedHat: [commentCreator] :: Comment -> UserEmail
- Web.Bugzilla.RedHat: [commentId] :: Comment -> !CommentId
- Web.Bugzilla.RedHat: [commentIsPrivate] :: Comment -> Bool
- Web.Bugzilla.RedHat: [commentText] :: Comment -> Text
- Web.Bugzilla.RedHat: [externalBugId] :: ExternalBug -> Text
- Web.Bugzilla.RedHat: [externalBzId] :: ExternalBug -> Int
- Web.Bugzilla.RedHat: [externalDescription] :: ExternalBug -> Text
- Web.Bugzilla.RedHat: [externalId] :: ExternalBug -> Int
- Web.Bugzilla.RedHat: [externalPriority] :: ExternalBug -> Text
- Web.Bugzilla.RedHat: [externalStatus] :: ExternalBug -> Text
- Web.Bugzilla.RedHat: [externalTypeDescription] :: ExternalType -> Text
- Web.Bugzilla.RedHat: [externalTypeFullUrl] :: ExternalType -> Text
- Web.Bugzilla.RedHat: [externalTypeId] :: ExternalType -> Int
- Web.Bugzilla.RedHat: [externalTypeType] :: ExternalType -> Text
- Web.Bugzilla.RedHat: [externalTypeUrl] :: ExternalType -> Text
- Web.Bugzilla.RedHat: [externalType] :: ExternalBug -> ExternalType
- Web.Bugzilla.RedHat: [flagCreationDate] :: Flag -> UTCTime
- Web.Bugzilla.RedHat: [flagId] :: Flag -> !FlagId
- Web.Bugzilla.RedHat: [flagModificationDate] :: Flag -> UTCTime
- Web.Bugzilla.RedHat: [flagName] :: Flag -> Text
- Web.Bugzilla.RedHat: [flagRequestee] :: Flag -> Maybe UserEmail
- Web.Bugzilla.RedHat: [flagSetter] :: Flag -> UserEmail
- Web.Bugzilla.RedHat: [flagStatus] :: Flag -> Text
- Web.Bugzilla.RedHat: [flagTypeId] :: Flag -> !FlagType
- Web.Bugzilla.RedHat: [historyBugId] :: History -> !BugId
- Web.Bugzilla.RedHat: [historyEventChanges] :: HistoryEvent -> [Change]
- Web.Bugzilla.RedHat: [historyEventId] :: HistoryEvent -> EventId
- Web.Bugzilla.RedHat: [historyEventTime] :: HistoryEvent -> UTCTime
- Web.Bugzilla.RedHat: [historyEventUser] :: HistoryEvent -> UserEmail
- Web.Bugzilla.RedHat: [historyEvents] :: History -> [HistoryEvent]
- Web.Bugzilla.RedHat: [modAdded] :: Modification a -> Maybe a
- Web.Bugzilla.RedHat: [modAttachmentId] :: Modification a -> Maybe AttachmentId
- Web.Bugzilla.RedHat: [modRemoved] :: Modification a -> Maybe a
- Web.Bugzilla.RedHat: [userEmail] :: User -> Maybe UserEmail
- Web.Bugzilla.RedHat: [userId] :: User -> !UserId
- Web.Bugzilla.RedHat: [userName] :: User -> Text
- Web.Bugzilla.RedHat: [userRealName] :: User -> Text
- Web.Bugzilla.RedHat: anonymousSession :: BugzillaContext -> BugzillaSession
- Web.Bugzilla.RedHat: apikeySession :: BugzillaContext -> BugzillaApikey -> BugzillaSession
- Web.Bugzilla.RedHat: data Attachment
- Web.Bugzilla.RedHat: data Bug
- Web.Bugzilla.RedHat: data BugzillaContext
- Web.Bugzilla.RedHat: data BugzillaException
- Web.Bugzilla.RedHat: data BugzillaSession
- Web.Bugzilla.RedHat: data Change
- Web.Bugzilla.RedHat: data Comment
- Web.Bugzilla.RedHat: data ExternalBug
- Web.Bugzilla.RedHat: data ExternalType
- Web.Bugzilla.RedHat: data Field a
- Web.Bugzilla.RedHat: data Flag
- Web.Bugzilla.RedHat: data History
- Web.Bugzilla.RedHat: data HistoryEvent
- Web.Bugzilla.RedHat: data (Eq a, Show a) => Modification a
- Web.Bugzilla.RedHat: data Request
- Web.Bugzilla.RedHat: data User
- Web.Bugzilla.RedHat: fieldName :: Field a -> Text
- Web.Bugzilla.RedHat: getAttachment :: BugzillaSession -> AttachmentId -> IO (Maybe Attachment)
- Web.Bugzilla.RedHat: getAttachments :: BugzillaSession -> BugId -> IO [Attachment]
- Web.Bugzilla.RedHat: getBug :: BugzillaSession -> BugId -> IO (Maybe Bug)
- Web.Bugzilla.RedHat: getBugAll :: BugzillaSession -> BugId -> IO (Maybe Bug)
- Web.Bugzilla.RedHat: getComments :: BugzillaSession -> BugId -> IO [Comment]
- Web.Bugzilla.RedHat: getHistory :: BugzillaSession -> BugId -> IO History
- Web.Bugzilla.RedHat: getUser :: BugzillaSession -> UserEmail -> IO (Maybe User)
- Web.Bugzilla.RedHat: getUserById :: BugzillaSession -> UserId -> IO (Maybe User)
- Web.Bugzilla.RedHat: intAsText :: Int -> Text
- Web.Bugzilla.RedHat: loginSession :: BugzillaContext -> UserEmail -> Text -> IO (Maybe BugzillaSession)
- Web.Bugzilla.RedHat: newBugzillaContext :: BugzillaServer -> IO BugzillaContext
- Web.Bugzilla.RedHat: newBzRequest :: BugzillaSession -> [Text] -> QueryText -> Request
- Web.Bugzilla.RedHat: newtype BugzillaApikey
- Web.Bugzilla.RedHat: newtype BugzillaToken
- Web.Bugzilla.RedHat: searchBugs :: BugzillaSession -> SearchExpression -> IO [Bug]
- Web.Bugzilla.RedHat: searchBugs' :: BugzillaSession -> SearchExpression -> IO [BugId]
- Web.Bugzilla.RedHat: searchBugsAll :: BugzillaSession -> SearchExpression -> IO [Bug]
- Web.Bugzilla.RedHat: searchBugsAllWithLimit :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [Bug]
- Web.Bugzilla.RedHat: searchBugsWithLimit :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [Bug]
- Web.Bugzilla.RedHat: searchBugsWithLimit' :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [BugId]
- Web.Bugzilla.RedHat: searchUsers :: BugzillaSession -> Text -> IO [User]
- Web.Bugzilla.RedHat: sendBzRequest :: FromJSON a => BugzillaSession -> Request -> IO a
- Web.Bugzilla.RedHat: type AttachmentId = Int
- Web.Bugzilla.RedHat: type BugId = Int
- Web.Bugzilla.RedHat: type BugzillaServer = Text
- Web.Bugzilla.RedHat: type CommentId = Int
- Web.Bugzilla.RedHat: type EventId = Int
- Web.Bugzilla.RedHat: type FlagId = Int
- Web.Bugzilla.RedHat: type FlagType = Int
- Web.Bugzilla.RedHat: type UserEmail = Text
- Web.Bugzilla.RedHat: type UserId = Int
- Web.Bugzilla.RedHat.Search: (.&&.) :: SearchExpression -> SearchExpression -> SearchExpression
- Web.Bugzilla.RedHat.Search: (./=.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (./=~.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.<.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.<=.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.==.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.=~.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.>.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.>=.) :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: (.||.) :: SearchExpression -> SearchExpression -> SearchExpression
- Web.Bugzilla.RedHat.Search: [AliasField] :: Field [Text]
- Web.Bugzilla.RedHat.Search: [AssignedToField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [AttachmentCreatorField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [AttachmentDataField] :: Field Text
- Web.Bugzilla.RedHat.Search: [AttachmentDescriptionField] :: Field Text
- Web.Bugzilla.RedHat.Search: [AttachmentFilenameField] :: Field Text
- Web.Bugzilla.RedHat.Search: [AttachmentIsObsoleteField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [AttachmentIsPatchField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [AttachmentIsPrivateField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [AttachmentMimetypeField] :: Field Text
- Web.Bugzilla.RedHat.Search: [BlocksField] :: Field Int
- Web.Bugzilla.RedHat.Search: [BugIdField] :: Field Int
- Web.Bugzilla.RedHat.Search: [BugURLField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CcField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [CcListAccessibleField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [ChangedField] :: Field UTCTime
- Web.Bugzilla.RedHat.Search: [ClassificationField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CommentCountField] :: Field Int
- Web.Bugzilla.RedHat.Search: [CommentField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CommentIsPrivateField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CommentTagsField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CommenterField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [ComponentField] :: Field [Text]
- Web.Bugzilla.RedHat.Search: [ContentField] :: Field Text
- Web.Bugzilla.RedHat.Search: [CreationDateField] :: Field UTCTime
- Web.Bugzilla.RedHat.Search: [CustomField] :: Text -> Field Text
- Web.Bugzilla.RedHat.Search: [DaysElapsedField] :: Field Int
- Web.Bugzilla.RedHat.Search: [DependsOnField] :: Field Int
- Web.Bugzilla.RedHat.Search: [EverConfirmedField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [FlagRequesteeField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [FlagSetterField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [FlagsField] :: Field Text
- Web.Bugzilla.RedHat.Search: [GroupField] :: Field Text
- Web.Bugzilla.RedHat.Search: [HardwareField] :: Field Text
- Web.Bugzilla.RedHat.Search: [KeywordsField] :: Field [Text]
- Web.Bugzilla.RedHat.Search: [OperatingSystemField] :: Field Text
- Web.Bugzilla.RedHat.Search: [PriorityField] :: Field Text
- Web.Bugzilla.RedHat.Search: [ProductField] :: Field Text
- Web.Bugzilla.RedHat.Search: [QaContactField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [ReporterAccessibleField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [ReporterField] :: Field UserEmail
- Web.Bugzilla.RedHat.Search: [ResolutionField] :: Field Text
- Web.Bugzilla.RedHat.Search: [RestrictCommentsField] :: Field Bool
- Web.Bugzilla.RedHat.Search: [SeeAlsoField] :: Field Text
- Web.Bugzilla.RedHat.Search: [SeverityField] :: Field Text
- Web.Bugzilla.RedHat.Search: [StatusField] :: Field Text
- Web.Bugzilla.RedHat.Search: [SummaryField] :: Field Text
- Web.Bugzilla.RedHat.Search: [TagsField] :: Field Text
- Web.Bugzilla.RedHat.Search: [TargetMilestoneField] :: Field Text
- Web.Bugzilla.RedHat.Search: [TimeSinceAssigneeTouchedField] :: Field Int
- Web.Bugzilla.RedHat.Search: [VersionField] :: Field Text
- Web.Bugzilla.RedHat.Search: [VotesField] :: Field Text
- Web.Bugzilla.RedHat.Search: [WhiteboardField] :: Field Text
- Web.Bugzilla.RedHat.Search: changedAfter :: FieldType a => Field a -> UTCTime -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedBefore :: FieldType a => Field a -> UTCTime -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedBy :: FieldType a => Field a -> UserEmail -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedFrom :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedRange :: UTCTime -> UTCTime -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedSince :: UTCTime -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedTo :: FieldType a => Field a -> a -> SearchExpression
- Web.Bugzilla.RedHat.Search: changedUntil :: UTCTime -> SearchExpression
- Web.Bugzilla.RedHat.Search: contains :: Field Text -> Text -> SearchExpression
- Web.Bugzilla.RedHat.Search: containsAll :: Field Text -> [Text] -> SearchExpression
- Web.Bugzilla.RedHat.Search: containsAny :: Field Text -> [Text] -> SearchExpression
- Web.Bugzilla.RedHat.Search: containsCase :: Field Text -> Text -> SearchExpression
- Web.Bugzilla.RedHat.Search: contentMatches :: Text -> SearchExpression
- Web.Bugzilla.RedHat.Search: data Field a
- Web.Bugzilla.RedHat.Search: data SearchExpression
- Web.Bugzilla.RedHat.Search: equalsAny :: FieldType a => Field a -> [a] -> SearchExpression
- Web.Bugzilla.RedHat.Search: evalSearchExpr :: SearchExpression -> [QueryPart]
- Web.Bugzilla.RedHat.Search: infix 4 .>=.
- Web.Bugzilla.RedHat.Search: infixr 2 .||.
- Web.Bugzilla.RedHat.Search: infixr 3 .&&.
- Web.Bugzilla.RedHat.Search: isEmpty :: FieldType a => Field a -> SearchExpression
- Web.Bugzilla.RedHat.Search: isNotEmpty :: FieldType a => Field a -> SearchExpression
- Web.Bugzilla.RedHat.Search: not' :: SearchExpression -> SearchExpression
+ Web.RedHatBugzilla: AnonymousSession :: BugzillaServer -> BugzillaSession
+ Web.RedHatBugzilla: ApiKeySession :: BugzillaServer -> BugzillaApiKey -> BugzillaSession
+ Web.RedHatBugzilla: Attachment :: !AttachmentId -> !BugId -> Text -> Text -> UserEmail -> Bool -> Bool -> Bool -> [Flag] -> UTCTime -> UTCTime -> Text -> !Int -> Text -> Attachment
+ Web.RedHatBugzilla: BoolFieldChange :: Field Bool -> Modification Bool -> Change
+ Web.RedHatBugzilla: Bug :: !BugId -> Maybe [Text] -> UserEmail -> User -> [BugId] -> [UserEmail] -> [User] -> Text -> [Text] -> UTCTime -> UserEmail -> User -> [BugId] -> Maybe BugId -> Maybe [Flag] -> [Text] -> Bool -> Bool -> Bool -> Bool -> [Text] -> UTCTime -> Text -> Text -> Text -> Text -> UserEmail -> Text -> [Text] -> Text -> Text -> Text -> Text -> Text -> [Text] -> Text -> KeyMap Text -> Maybe [ExternalBug] -> Bug
+ Web.RedHatBugzilla: BugzillaAPIError :: Int -> String -> BugzillaException
+ Web.RedHatBugzilla: BugzillaApiKey :: Text -> BugzillaApiKey
+ Web.RedHatBugzilla: BugzillaJSONParseError :: String -> BugzillaException
+ Web.RedHatBugzilla: BugzillaUnexpectedValue :: String -> BugzillaException
+ Web.RedHatBugzilla: Comment :: !CommentId -> !BugId -> Maybe AttachmentId -> !Int -> Text -> UserEmail -> UTCTime -> Bool -> Comment
+ Web.RedHatBugzilla: ExternalBug :: Text -> Int -> Text -> Text -> Text -> Int -> ExternalType -> ExternalBug
+ Web.RedHatBugzilla: ExternalType :: Text -> Text -> Int -> Text -> Text -> ExternalType
+ Web.RedHatBugzilla: Flag :: !FlagId -> !FlagType -> Text -> UserEmail -> Text -> UTCTime -> UTCTime -> Maybe UserEmail -> Flag
+ Web.RedHatBugzilla: History :: !BugId -> [HistoryEvent] -> History
+ Web.RedHatBugzilla: HistoryEvent :: EventId -> UTCTime -> UserEmail -> [Change] -> HistoryEvent
+ Web.RedHatBugzilla: IntFieldChange :: Field Int -> Modification Int -> Change
+ Web.RedHatBugzilla: ListFieldChange :: Field [Text] -> Modification [Text] -> Change
+ Web.RedHatBugzilla: Modification :: Maybe a -> Maybe a -> Maybe AttachmentId -> Modification a
+ Web.RedHatBugzilla: TextFieldChange :: Field Text -> Modification Text -> Change
+ Web.RedHatBugzilla: TimeFieldChange :: Field UTCTime -> Modification UTCTime -> Change
+ Web.RedHatBugzilla: User :: !UserId -> Maybe UserEmail -> Text -> Text -> User
+ Web.RedHatBugzilla: [AliasField] :: Field [Text]
+ Web.RedHatBugzilla: [AssignedToField] :: Field UserEmail
+ Web.RedHatBugzilla: [AttachmentCreatorField] :: Field UserEmail
+ Web.RedHatBugzilla: [AttachmentDataField] :: Field Text
+ Web.RedHatBugzilla: [AttachmentDescriptionField] :: Field Text
+ Web.RedHatBugzilla: [AttachmentFilenameField] :: Field Text
+ Web.RedHatBugzilla: [AttachmentIsObsoleteField] :: Field Bool
+ Web.RedHatBugzilla: [AttachmentIsPatchField] :: Field Bool
+ Web.RedHatBugzilla: [AttachmentIsPrivateField] :: Field Bool
+ Web.RedHatBugzilla: [AttachmentMimetypeField] :: Field Text
+ Web.RedHatBugzilla: [BlocksField] :: Field Int
+ Web.RedHatBugzilla: [BugIdField] :: Field Int
+ Web.RedHatBugzilla: [BugURLField] :: Field Text
+ Web.RedHatBugzilla: [CcField] :: Field UserEmail
+ Web.RedHatBugzilla: [CcListAccessibleField] :: Field Bool
+ Web.RedHatBugzilla: [ChangedField] :: Field UTCTime
+ Web.RedHatBugzilla: [ClassificationField] :: Field Text
+ Web.RedHatBugzilla: [CommentCountField] :: Field Int
+ Web.RedHatBugzilla: [CommentField] :: Field Text
+ Web.RedHatBugzilla: [CommentIsPrivateField] :: Field Text
+ Web.RedHatBugzilla: [CommentTagsField] :: Field Text
+ Web.RedHatBugzilla: [CommenterField] :: Field UserEmail
+ Web.RedHatBugzilla: [ComponentField] :: Field [Text]
+ Web.RedHatBugzilla: [ContentField] :: Field Text
+ Web.RedHatBugzilla: [CreationDateField] :: Field UTCTime
+ Web.RedHatBugzilla: [CustomField] :: Text -> Field Text
+ Web.RedHatBugzilla: [DaysElapsedField] :: Field Int
+ Web.RedHatBugzilla: [DependsOnField] :: Field Int
+ Web.RedHatBugzilla: [EverConfirmedField] :: Field Bool
+ Web.RedHatBugzilla: [FlagRequesteeField] :: Field UserEmail
+ Web.RedHatBugzilla: [FlagSetterField] :: Field UserEmail
+ Web.RedHatBugzilla: [FlagsField] :: Field Text
+ Web.RedHatBugzilla: [GroupField] :: Field Text
+ Web.RedHatBugzilla: [HardwareField] :: Field Text
+ Web.RedHatBugzilla: [KeywordsField] :: Field [Text]
+ Web.RedHatBugzilla: [OperatingSystemField] :: Field Text
+ Web.RedHatBugzilla: [PriorityField] :: Field Text
+ Web.RedHatBugzilla: [ProductField] :: Field Text
+ Web.RedHatBugzilla: [QaContactField] :: Field UserEmail
+ Web.RedHatBugzilla: [ReporterAccessibleField] :: Field Bool
+ Web.RedHatBugzilla: [ReporterField] :: Field UserEmail
+ Web.RedHatBugzilla: [ResolutionField] :: Field Text
+ Web.RedHatBugzilla: [RestrictCommentsField] :: Field Bool
+ Web.RedHatBugzilla: [SeeAlsoField] :: Field Text
+ Web.RedHatBugzilla: [SeverityField] :: Field Text
+ Web.RedHatBugzilla: [StatusField] :: Field Text
+ Web.RedHatBugzilla: [SummaryField] :: Field Text
+ Web.RedHatBugzilla: [TagsField] :: Field Text
+ Web.RedHatBugzilla: [TargetMilestoneField] :: Field Text
+ Web.RedHatBugzilla: [TimeSinceAssigneeTouchedField] :: Field Int
+ Web.RedHatBugzilla: [VersionField] :: Field Text
+ Web.RedHatBugzilla: [VotesField] :: Field Text
+ Web.RedHatBugzilla: [WhiteboardField] :: Field Text
+ Web.RedHatBugzilla: [attachmentBugId] :: Attachment -> !BugId
+ Web.RedHatBugzilla: [attachmentContentType] :: Attachment -> Text
+ Web.RedHatBugzilla: [attachmentCreationTime] :: Attachment -> UTCTime
+ Web.RedHatBugzilla: [attachmentCreator] :: Attachment -> UserEmail
+ Web.RedHatBugzilla: [attachmentData] :: Attachment -> Text
+ Web.RedHatBugzilla: [attachmentFileName] :: Attachment -> Text
+ Web.RedHatBugzilla: [attachmentFlags] :: Attachment -> [Flag]
+ Web.RedHatBugzilla: [attachmentId] :: Attachment -> !AttachmentId
+ Web.RedHatBugzilla: [attachmentIsObsolete] :: Attachment -> Bool
+ Web.RedHatBugzilla: [attachmentIsPatch] :: Attachment -> Bool
+ Web.RedHatBugzilla: [attachmentIsPrivate] :: Attachment -> Bool
+ Web.RedHatBugzilla: [attachmentLastChangeTime] :: Attachment -> UTCTime
+ Web.RedHatBugzilla: [attachmentSize] :: Attachment -> !Int
+ Web.RedHatBugzilla: [attachmentSummary] :: Attachment -> Text
+ Web.RedHatBugzilla: [bugAlias] :: Bug -> Maybe [Text]
+ Web.RedHatBugzilla: [bugAssignedToDetail] :: Bug -> User
+ Web.RedHatBugzilla: [bugAssignedTo] :: Bug -> UserEmail
+ Web.RedHatBugzilla: [bugBlocks] :: Bug -> [BugId]
+ Web.RedHatBugzilla: [bugCcDetail] :: Bug -> [User]
+ Web.RedHatBugzilla: [bugCc] :: Bug -> [UserEmail]
+ Web.RedHatBugzilla: [bugClassification] :: Bug -> Text
+ Web.RedHatBugzilla: [bugComponent] :: Bug -> [Text]
+ Web.RedHatBugzilla: [bugCreationTime] :: Bug -> UTCTime
+ Web.RedHatBugzilla: [bugCreatorDetail] :: Bug -> User
+ Web.RedHatBugzilla: [bugCreator] :: Bug -> UserEmail
+ Web.RedHatBugzilla: [bugCustomFields] :: Bug -> KeyMap Text
+ Web.RedHatBugzilla: [bugDependsOn] :: Bug -> [BugId]
+ Web.RedHatBugzilla: [bugDupeOf] :: Bug -> Maybe BugId
+ Web.RedHatBugzilla: [bugExternalBugs] :: Bug -> Maybe [ExternalBug]
+ Web.RedHatBugzilla: [bugFlags] :: Bug -> Maybe [Flag]
+ Web.RedHatBugzilla: [bugGroups] :: Bug -> [Text]
+ Web.RedHatBugzilla: [bugId] :: Bug -> !BugId
+ Web.RedHatBugzilla: [bugIsCcAccessible] :: Bug -> Bool
+ Web.RedHatBugzilla: [bugIsConfirmed] :: Bug -> Bool
+ Web.RedHatBugzilla: [bugIsCreatorAccessible] :: Bug -> Bool
+ Web.RedHatBugzilla: [bugIsOpen] :: Bug -> Bool
+ Web.RedHatBugzilla: [bugKeywords] :: Bug -> [Text]
+ Web.RedHatBugzilla: [bugLastChangeTime] :: Bug -> UTCTime
+ Web.RedHatBugzilla: [bugOpSys] :: Bug -> Text
+ Web.RedHatBugzilla: [bugPlatform] :: Bug -> Text
+ Web.RedHatBugzilla: [bugPriority] :: Bug -> Text
+ Web.RedHatBugzilla: [bugProduct] :: Bug -> Text
+ Web.RedHatBugzilla: [bugQaContact] :: Bug -> UserEmail
+ Web.RedHatBugzilla: [bugResolution] :: Bug -> Text
+ Web.RedHatBugzilla: [bugSeeAlso] :: Bug -> [Text]
+ Web.RedHatBugzilla: [bugSeverity] :: Bug -> Text
+ Web.RedHatBugzilla: [bugStatus] :: Bug -> Text
+ Web.RedHatBugzilla: [bugSummary] :: Bug -> Text
+ Web.RedHatBugzilla: [bugTargetMilestone] :: Bug -> Text
+ Web.RedHatBugzilla: [bugUrl] :: Bug -> Text
+ Web.RedHatBugzilla: [bugVersion] :: Bug -> [Text]
+ Web.RedHatBugzilla: [bugWhiteboard] :: Bug -> Text
+ Web.RedHatBugzilla: [commentAttachmentId] :: Comment -> Maybe AttachmentId
+ Web.RedHatBugzilla: [commentBugId] :: Comment -> !BugId
+ Web.RedHatBugzilla: [commentCount] :: Comment -> !Int
+ Web.RedHatBugzilla: [commentCreationTime] :: Comment -> UTCTime
+ Web.RedHatBugzilla: [commentCreator] :: Comment -> UserEmail
+ Web.RedHatBugzilla: [commentId] :: Comment -> !CommentId
+ Web.RedHatBugzilla: [commentIsPrivate] :: Comment -> Bool
+ Web.RedHatBugzilla: [commentText] :: Comment -> Text
+ Web.RedHatBugzilla: [externalBugId] :: ExternalBug -> Text
+ Web.RedHatBugzilla: [externalBzId] :: ExternalBug -> Int
+ Web.RedHatBugzilla: [externalDescription] :: ExternalBug -> Text
+ Web.RedHatBugzilla: [externalId] :: ExternalBug -> Int
+ Web.RedHatBugzilla: [externalPriority] :: ExternalBug -> Text
+ Web.RedHatBugzilla: [externalStatus] :: ExternalBug -> Text
+ Web.RedHatBugzilla: [externalTypeDescription] :: ExternalType -> Text
+ Web.RedHatBugzilla: [externalTypeFullUrl] :: ExternalType -> Text
+ Web.RedHatBugzilla: [externalTypeId] :: ExternalType -> Int
+ Web.RedHatBugzilla: [externalTypeType] :: ExternalType -> Text
+ Web.RedHatBugzilla: [externalTypeUrl] :: ExternalType -> Text
+ Web.RedHatBugzilla: [externalType] :: ExternalBug -> ExternalType
+ Web.RedHatBugzilla: [flagCreationDate] :: Flag -> UTCTime
+ Web.RedHatBugzilla: [flagId] :: Flag -> !FlagId
+ Web.RedHatBugzilla: [flagModificationDate] :: Flag -> UTCTime
+ Web.RedHatBugzilla: [flagName] :: Flag -> Text
+ Web.RedHatBugzilla: [flagRequestee] :: Flag -> Maybe UserEmail
+ Web.RedHatBugzilla: [flagSetter] :: Flag -> UserEmail
+ Web.RedHatBugzilla: [flagStatus] :: Flag -> Text
+ Web.RedHatBugzilla: [flagTypeId] :: Flag -> !FlagType
+ Web.RedHatBugzilla: [historyBugId] :: History -> !BugId
+ Web.RedHatBugzilla: [historyEventChanges] :: HistoryEvent -> [Change]
+ Web.RedHatBugzilla: [historyEventId] :: HistoryEvent -> EventId
+ Web.RedHatBugzilla: [historyEventTime] :: HistoryEvent -> UTCTime
+ Web.RedHatBugzilla: [historyEventUser] :: HistoryEvent -> UserEmail
+ Web.RedHatBugzilla: [historyEvents] :: History -> [HistoryEvent]
+ Web.RedHatBugzilla: [modAdded] :: Modification a -> Maybe a
+ Web.RedHatBugzilla: [modAttachmentId] :: Modification a -> Maybe AttachmentId
+ Web.RedHatBugzilla: [modRemoved] :: Modification a -> Maybe a
+ Web.RedHatBugzilla: [userEmail] :: User -> Maybe UserEmail
+ Web.RedHatBugzilla: [userId] :: User -> !UserId
+ Web.RedHatBugzilla: [userName] :: User -> Text
+ Web.RedHatBugzilla: [userRealName] :: User -> Text
+ Web.RedHatBugzilla: anonymousSession :: BugzillaServer -> BugzillaSession
+ Web.RedHatBugzilla: apikeySession :: BugzillaServer -> BugzillaApiKey -> BugzillaSession
+ Web.RedHatBugzilla: data Attachment
+ Web.RedHatBugzilla: data Bug
+ Web.RedHatBugzilla: data BugzillaException
+ Web.RedHatBugzilla: data BugzillaSession
+ Web.RedHatBugzilla: data Change
+ Web.RedHatBugzilla: data Comment
+ Web.RedHatBugzilla: data ExternalBug
+ Web.RedHatBugzilla: data ExternalType
+ Web.RedHatBugzilla: data Field a
+ Web.RedHatBugzilla: data Flag
+ Web.RedHatBugzilla: data History
+ Web.RedHatBugzilla: data HistoryEvent
+ Web.RedHatBugzilla: data (Eq a, Show a) => Modification a
+ Web.RedHatBugzilla: data Request
+ Web.RedHatBugzilla: data User
+ Web.RedHatBugzilla: fieldName :: Field a -> Text
+ Web.RedHatBugzilla: getAttachment :: BugzillaSession -> AttachmentId -> IO (Maybe Attachment)
+ Web.RedHatBugzilla: getAttachments :: BugzillaSession -> BugId -> IO [Attachment]
+ Web.RedHatBugzilla: getBug :: BugzillaSession -> BugId -> IO (Maybe Bug)
+ Web.RedHatBugzilla: getBugAll :: BugzillaSession -> BugId -> IO (Maybe Bug)
+ Web.RedHatBugzilla: getComments :: BugzillaSession -> BugId -> IO [Comment]
+ Web.RedHatBugzilla: getHistory :: BugzillaSession -> BugId -> IO History
+ Web.RedHatBugzilla: getUser :: BugzillaSession -> UserEmail -> IO (Maybe User)
+ Web.RedHatBugzilla: getUserById :: BugzillaSession -> UserId -> IO (Maybe User)
+ Web.RedHatBugzilla: intAsText :: Int -> Text
+ Web.RedHatBugzilla: newBzRequest :: BugzillaSession -> [Text] -> QueryText -> Request
+ Web.RedHatBugzilla: newtype BugzillaApiKey
+ Web.RedHatBugzilla: searchBugs :: BugzillaSession -> SearchExpression -> IO [Bug]
+ Web.RedHatBugzilla: searchBugs' :: BugzillaSession -> SearchExpression -> IO [BugId]
+ Web.RedHatBugzilla: searchBugsAll :: BugzillaSession -> SearchExpression -> IO [Bug]
+ Web.RedHatBugzilla: searchBugsAllWithLimit :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [Bug]
+ Web.RedHatBugzilla: searchBugsWithLimit :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [Bug]
+ Web.RedHatBugzilla: searchBugsWithLimit' :: BugzillaSession -> Int -> Int -> SearchExpression -> IO [BugId]
+ Web.RedHatBugzilla: searchUsers :: BugzillaSession -> Text -> IO [User]
+ Web.RedHatBugzilla: sendBzRequest :: FromJSON a => Request -> IO a
+ Web.RedHatBugzilla: type AttachmentId = Int
+ Web.RedHatBugzilla: type BugId = Int
+ Web.RedHatBugzilla: type BugzillaServer = Text
+ Web.RedHatBugzilla: type CommentId = Int
+ Web.RedHatBugzilla: type EventId = Int
+ Web.RedHatBugzilla: type FlagId = Int
+ Web.RedHatBugzilla: type FlagType = Int
+ Web.RedHatBugzilla: type UserEmail = Text
+ Web.RedHatBugzilla: type UserId = Int
+ Web.RedHatBugzilla.Search: (.&&.) :: SearchExpression -> SearchExpression -> SearchExpression
+ Web.RedHatBugzilla.Search: (./=.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (./=~.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.<.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.<=.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.==.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.=~.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.>.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.>=.) :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: (.||.) :: SearchExpression -> SearchExpression -> SearchExpression
+ Web.RedHatBugzilla.Search: [AliasField] :: Field [Text]
+ Web.RedHatBugzilla.Search: [AssignedToField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [AttachmentCreatorField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [AttachmentDataField] :: Field Text
+ Web.RedHatBugzilla.Search: [AttachmentDescriptionField] :: Field Text
+ Web.RedHatBugzilla.Search: [AttachmentFilenameField] :: Field Text
+ Web.RedHatBugzilla.Search: [AttachmentIsObsoleteField] :: Field Bool
+ Web.RedHatBugzilla.Search: [AttachmentIsPatchField] :: Field Bool
+ Web.RedHatBugzilla.Search: [AttachmentIsPrivateField] :: Field Bool
+ Web.RedHatBugzilla.Search: [AttachmentMimetypeField] :: Field Text
+ Web.RedHatBugzilla.Search: [BlocksField] :: Field Int
+ Web.RedHatBugzilla.Search: [BugIdField] :: Field Int
+ Web.RedHatBugzilla.Search: [BugURLField] :: Field Text
+ Web.RedHatBugzilla.Search: [CcField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [CcListAccessibleField] :: Field Bool
+ Web.RedHatBugzilla.Search: [ChangedField] :: Field UTCTime
+ Web.RedHatBugzilla.Search: [ClassificationField] :: Field Text
+ Web.RedHatBugzilla.Search: [CommentCountField] :: Field Int
+ Web.RedHatBugzilla.Search: [CommentField] :: Field Text
+ Web.RedHatBugzilla.Search: [CommentIsPrivateField] :: Field Text
+ Web.RedHatBugzilla.Search: [CommentTagsField] :: Field Text
+ Web.RedHatBugzilla.Search: [CommenterField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [ComponentField] :: Field [Text]
+ Web.RedHatBugzilla.Search: [ContentField] :: Field Text
+ Web.RedHatBugzilla.Search: [CreationDateField] :: Field UTCTime
+ Web.RedHatBugzilla.Search: [CustomField] :: Text -> Field Text
+ Web.RedHatBugzilla.Search: [DaysElapsedField] :: Field Int
+ Web.RedHatBugzilla.Search: [DependsOnField] :: Field Int
+ Web.RedHatBugzilla.Search: [EverConfirmedField] :: Field Bool
+ Web.RedHatBugzilla.Search: [FlagRequesteeField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [FlagSetterField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [FlagsField] :: Field Text
+ Web.RedHatBugzilla.Search: [GroupField] :: Field Text
+ Web.RedHatBugzilla.Search: [HardwareField] :: Field Text
+ Web.RedHatBugzilla.Search: [KeywordsField] :: Field [Text]
+ Web.RedHatBugzilla.Search: [OperatingSystemField] :: Field Text
+ Web.RedHatBugzilla.Search: [PriorityField] :: Field Text
+ Web.RedHatBugzilla.Search: [ProductField] :: Field Text
+ Web.RedHatBugzilla.Search: [QaContactField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [ReporterAccessibleField] :: Field Bool
+ Web.RedHatBugzilla.Search: [ReporterField] :: Field UserEmail
+ Web.RedHatBugzilla.Search: [ResolutionField] :: Field Text
+ Web.RedHatBugzilla.Search: [RestrictCommentsField] :: Field Bool
+ Web.RedHatBugzilla.Search: [SeeAlsoField] :: Field Text
+ Web.RedHatBugzilla.Search: [SeverityField] :: Field Text
+ Web.RedHatBugzilla.Search: [StatusField] :: Field Text
+ Web.RedHatBugzilla.Search: [SummaryField] :: Field Text
+ Web.RedHatBugzilla.Search: [TagsField] :: Field Text
+ Web.RedHatBugzilla.Search: [TargetMilestoneField] :: Field Text
+ Web.RedHatBugzilla.Search: [TimeSinceAssigneeTouchedField] :: Field Int
+ Web.RedHatBugzilla.Search: [VersionField] :: Field Text
+ Web.RedHatBugzilla.Search: [VotesField] :: Field Text
+ Web.RedHatBugzilla.Search: [WhiteboardField] :: Field Text
+ Web.RedHatBugzilla.Search: changedAfter :: FieldType a => Field a -> UTCTime -> SearchExpression
+ Web.RedHatBugzilla.Search: changedBefore :: FieldType a => Field a -> UTCTime -> SearchExpression
+ Web.RedHatBugzilla.Search: changedBy :: FieldType a => Field a -> UserEmail -> SearchExpression
+ Web.RedHatBugzilla.Search: changedFrom :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: changedRange :: UTCTime -> UTCTime -> SearchExpression
+ Web.RedHatBugzilla.Search: changedSince :: UTCTime -> SearchExpression
+ Web.RedHatBugzilla.Search: changedTo :: FieldType a => Field a -> a -> SearchExpression
+ Web.RedHatBugzilla.Search: changedUntil :: UTCTime -> SearchExpression
+ Web.RedHatBugzilla.Search: contains :: Field Text -> Text -> SearchExpression
+ Web.RedHatBugzilla.Search: containsAll :: Field Text -> [Text] -> SearchExpression
+ Web.RedHatBugzilla.Search: containsAny :: Field Text -> [Text] -> SearchExpression
+ Web.RedHatBugzilla.Search: containsCase :: Field Text -> Text -> SearchExpression
+ Web.RedHatBugzilla.Search: contentMatches :: Text -> SearchExpression
+ Web.RedHatBugzilla.Search: data Field a
+ Web.RedHatBugzilla.Search: data SearchExpression
+ Web.RedHatBugzilla.Search: equalsAny :: FieldType a => Field a -> [a] -> SearchExpression
+ Web.RedHatBugzilla.Search: evalSearchExpr :: SearchExpression -> [QueryPart]
+ Web.RedHatBugzilla.Search: infix 4 .>=.
+ Web.RedHatBugzilla.Search: infixr 2 .||.
+ Web.RedHatBugzilla.Search: infixr 3 .&&.
+ Web.RedHatBugzilla.Search: isEmpty :: FieldType a => Field a -> SearchExpression
+ Web.RedHatBugzilla.Search: isNotEmpty :: FieldType a => Field a -> SearchExpression
+ Web.RedHatBugzilla.Search: not' :: SearchExpression -> SearchExpression

Files

ChangeLog.md view
@@ -1,5 +1,17 @@ # bugzilla-redhat version history +## 1.0.0 (2022-02-20)+- rename Web.Bugzilla.RedHat to Web.RedHatBugzilla+- use Network.HTTP.Simple global session Manager+- from 28 Feb 2022 Red Hat Bugzilla only allows API key authentication:+- remove newBugzillaContext and BugzillaContext+- drop loginSession and also BugzillaToken+- bzServer replaces bzContext+- newBzRequest now puts API key in Authorization header+- sendBzRequest no longer takes a session argument+- rename BugzillaApikey to BugzillaApiKey+- rename ApikeySession to ApiKeySession+ ## 0.3.3 (2021-10-14) - support building with aeson-2.0 
TODO view
@@ -2,6 +2,4 @@  - instance Monoid SearchExpression? -- rename Bugzilla.Redhat to RedHatBugzilla for 0.4--- use http-query (http-conduit Simple)+* use http-query
bugzilla-redhat.cabal view
@@ -1,11 +1,11 @@ name:                bugzilla-redhat-version:             0.3.3+version:             1.0.0 synopsis:            A Haskell interface to the Bugzilla native REST API description:         This package is designed to provide an easy-to-use,                      type-safe interface to querying Bugzilla from Haskell.                      .                      This is a friendly fork of Seth Fowler's library,-                     with minor updates and API tweaks needed for+                     with updates and API tweaks needed for                      bugzilla.redhat.com. homepage:            https://github.com/juhp/hsbugzilla license:             BSD3@@ -13,7 +13,7 @@ author:              Seth Fowler <mark.seth.fowler@gmail.com> maintainer:          Jens Petersen <juhpetersen@gmail.com> copyright:           2014 Seth Fowler,-                     2020-2021 Jens Petersen+                     2020-2022 Jens Petersen category:            Web build-type:          Simple extra-source-files:  README.md@@ -31,11 +31,11 @@  library   ghc-options:         -Wall-  exposed-modules:     Web.Bugzilla.RedHat,-                       Web.Bugzilla.RedHat.Search-  other-modules:       Web.Bugzilla.RedHat.Internal.Network,-                       Web.Bugzilla.RedHat.Internal.Search,-                       Web.Bugzilla.RedHat.Internal.Types+  exposed-modules:     Web.RedHatBugzilla,+                       Web.RedHatBugzilla.Search+  other-modules:       Web.RedHatBugzilla.Internal.Network,+                       Web.RedHatBugzilla.Internal.Search,+                       Web.RedHatBugzilla.Internal.Types   build-depends:       base >=4.6 && <5,                        aeson >=0.7 && <2.1,                        blaze-builder >=0.3 && <0.5,
demo/BugzillaDemo.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -import Control.Exception (bracket) import Control.Monad import Data.Maybe import qualified Data.Text as T@@ -9,25 +8,23 @@ import System.Environment (getArgs) import System.IO -import Web.Bugzilla.RedHat-import Web.Bugzilla.RedHat.Search+import Web.RedHatBugzilla+import Web.RedHatBugzilla.Search  main :: IO ()-main = dispatch Nothing Nothing =<< getArgs+main = dispatch Nothing =<< getArgs -dispatch :: Maybe UserEmail -> Maybe BugzillaServer -> [String] -> IO ()-dispatch Nothing s ("--login" : user : as)    = dispatch (Just $ T.pack user) s as-dispatch l Nothing ("--server" : server : as) = dispatch l (Just $ T.pack server) as-dispatch l s ["--assigned-to", user]          = withBz l s $ doAssignedTo (T.pack user)-dispatch l s ["--assigned-to-brief", user]    = withBz l s $ doAssignedToBrief (T.pack user)-dispatch l s ["--requests", user]             = withBz l s $ doRequests (T.pack user)-dispatch l s ["--history", bug, n]            = withBz l s $ doHistory (read bug) (read n)-dispatch _ _ _                                = usage+dispatch :: Maybe BugzillaServer -> [String] -> IO ()+dispatch Nothing ("--server" : server : as) = dispatch (Just $ T.pack server) as+dispatch s ["--assigned-to", user]          = withBz s $ doAssignedTo (T.pack user)+dispatch s ["--assigned-to-brief", user]    = withBz s $ doAssignedToBrief (T.pack user)+dispatch s ["--requests", user]             = withBz s $ doRequests (T.pack user)+dispatch s ["--history", bug, n]            = withBz s $ doHistory (read bug) (read n)+dispatch _ _                                = usage  usage :: IO () usage = hPutStrLn stderr "Connection options:"      >> hPutStrLn stderr "  --server [domain name] - REQUIRED. The Bugzilla server to access."-     >> hPutStrLn stderr "  --login [user email]   - The user to log in with."      >> hPutStrLn stderr ""      >> hPutStrLn stderr "Bugzilla queries:"      >> hPutStrLn stderr "  --assigned-to [user email] - List bugs assigned to the user."@@ -35,22 +32,13 @@      >> hPutStrLn stderr "  --requests [user email]    - List requests for the user."      >> hPutStrLn stderr "  --history [bug number] [n] - List the most recent 'n' changes to the bug." -withBz :: Maybe UserEmail -> Maybe BugzillaServer -> (BugzillaSession -> IO ()) -> IO ()-withBz mLogin mServer f = do+withBz :: Maybe BugzillaServer -> (BugzillaSession -> IO ())+       -> IO ()+withBz mServer f = do   let server = case mServer of                  Just s  -> s                  Nothing -> error "Please specify a server with '--server'"-  ctx <- newBugzillaContext server-  case mLogin of-    Just login -> do hPutStrLn stderr "Enter password: "-                     password <- T.pack <$> withEcho False getLine-                     mSession <- loginSession ctx login password-                     case mSession of-                       Just session -> do hPutStrLn stderr "Login successful."-                                          f session-                       Nothing      -> do hPutStrLn stderr "Login failed. Falling back to anonymous session."-                                          f $ anonymousSession ctx-    Nothing -> f $ anonymousSession ctx+  f $ anonymousSession server   doAssignedTo :: UserEmail -> BugzillaSession -> IO ()@@ -158,9 +146,3 @@     showAid :: Maybe AttachmentId -> String     showAid (Just aid) = " (Attachment " ++ show aid ++ ")"     showAid Nothing    = ""--withEcho :: Bool -> IO a -> IO a-withEcho echo action =-    bracket (hGetEcho stdin)-            (hSetEcho stdin)-            (const $ hSetEcho stdin echo >> action)
− src/Web/Bugzilla/RedHat.hs
@@ -1,283 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}---- | This package is designed to provide an easy-to-use, typesafe---   interface to querying Bugzilla from Haskell.------   A modified version of Web.Bugzilla to support---   the list fields in Red Hat's modified bugzilla API.------   A very simple program using this package might look like this:------ >   ctx <- newBugzillaContext "https://bugzilla.redhat.com"--- >   let session = anonymousSession ctx--- >       user = "me@example.org"--- >       query = AssignedToField .==. user .&&.--- >               FlagRequesteeField .==. user .&&.--- >               (FlagsField `contains` "review" .||. FlagsField `contains` "feedback")--- >   bugs <- searchBugs session query--- >   mapM_ (putStrLn . show . bugSummary) bugs------   There's a somewhat more in-depth demo program included with the---   source code to this package.--module Web.Bugzilla.RedHat-( -- * Connecting to Bugzilla-  newBugzillaContext-, loginSession-, apikeySession-, anonymousSession--, BugzillaServer-, BugzillaContext-, BugzillaSession (..)-, BugzillaToken (..)-, BugzillaApikey (..)--  -- * Querying Bugzilla-, searchBugs-, searchBugsAll-, searchBugs'-, searchBugsWithLimit-, searchBugsAllWithLimit-, searchBugsWithLimit'-, getBug-, getBugAll-, getAttachment-, getAttachments-, getComments-, getHistory-, searchUsers-, getUser-, getUserById-, newBzRequest-, sendBzRequest-, intAsText--, Request-, BugId-, AttachmentId-, CommentId-, UserId-, EventId-, FlagId-, FlagType-, UserEmail-, Field (..)-, User (..)-, Flag (..)-, Bug (..)-, ExternalBug (..)-, ExternalType (..)-, Attachment (..)-, Comment (..)-, History (..)-, HistoryEvent (..)-, Change (..)-, Modification (..)-, fieldName--, BugzillaException (..)-) where--import Control.Exception (throw, try)-import Control.Monad.IO.Class (liftIO)-import Data.Aeson (FromJSON)-import qualified Data.Text as T-import Network.Connection (TLSSettings(..))-import Network.HTTP.Conduit (mkManagerSettings, newManager)--import Web.Bugzilla.RedHat.Internal.Network-import Web.Bugzilla.RedHat.Internal.Search-import Web.Bugzilla.RedHat.Internal.Types---- | Creates a new 'BugzillaContext', suitable for connecting to the---   provided server. You should try to reuse 'BugzillaContext's---   whenever possible, because creating them is expensive.-newBugzillaContext :: BugzillaServer -> IO BugzillaContext-newBugzillaContext server = do-  let settings = mkManagerSettings (TLSSettingsSimple True False False) Nothing-  manager <- liftIO $ newManager settings-  return $ BugzillaContext server manager---- | Attempts to create a logged-in 'BugzillaSession' using the---   provided username and password. Returns 'Nothing' if login---   fails.-loginSession :: BugzillaContext -> UserEmail -> T.Text -> IO (Maybe BugzillaSession)-loginSession ctx user password = do-  let loginQuery = [("login", Just user),-                    ("password", Just password)]-      session = anonymousSession ctx-      req = newBzRequest session ["login"] loginQuery-  eToken <- try $ sendBzRequest session req-  return $ case eToken of-             Left (BugzillaAPIError 300 _) -> Nothing-             Left e                        -> throw e-             Right token                   -> Just $ LoginSession ctx token---- | Creates a 'BugzillaSession' using the provided api key.-apikeySession :: BugzillaContext -> BugzillaApikey -> BugzillaSession-apikeySession = ApikeySession---- | Creates an anonymous 'BugzillaSession'. Note that some content---   will be hidden by Bugzilla when you make queries in this state.-anonymousSession :: BugzillaContext -> BugzillaSession-anonymousSession = AnonymousSession--intAsText :: Int -> T.Text-intAsText = T.pack . show---- | Searches Bugzilla and returns a list of 'Bug's. The 'SearchExpression'--- can be constructed conveniently using the operators in "Web.Bugzilla.Search".-searchBugs :: BugzillaSession -> SearchExpression -> IO [Bug]-searchBugs session search = do-  BugList bugs <- doSearchBugs session search Nothing Nothing-  return bugs---- | Similar to 'searchBugs', but return _all fields.-searchBugsAll :: BugzillaSession -> SearchExpression -> IO [Bug]-searchBugsAll session search = do-  BugList bugs <- doSearchBugs session search (Just "_all") Nothing-  return bugs---- | Like 'searchBugs', but returns a list of 'BugId's. You can--- retrieve the 'Bug' for each 'BugId' using 'getBug'. The combination--- of 'searchBugs'' and 'getBug' is much less efficient than--- 'searchBugs'. 'searchBugs'' is suitable for cases where you won't need to call--- 'getBug' most of the time - for example, polling to determine whether the--- set of bugs returned by a query has changed.-searchBugs' :: BugzillaSession -> SearchExpression -> IO [BugId]-searchBugs' session search = do-  BugIdList bugs <- doSearchBugs session search (Just "id") Nothing-  return bugs--doSearchBugs :: FromJSON a => BugzillaSession -> SearchExpression -> Maybe T.Text -> Maybe (Int, Int) -> IO a-doSearchBugs session search includeField limits = do-  let fieldsQuery = case includeField of-        Nothing -> []-        Just field -> [("include_fields", Just field)]-      limitQuery = case limits of-        Nothing -> []-        Just (limit, offset) -> [("limit", Just $ intAsText limit),-                                 ("offset", Just $ intAsText offset)]-      searchQuery = evalSearchExpr search-      req = newBzRequest session ["bug"] (limitQuery ++ fieldsQuery ++ searchQuery)-  sendBzRequest session req---- | Search Bugzilla and returns a limited number of results. You can---   call this repeatedly and use 'offset' to retrieve the results of---   a large query incrementally. Note that most Bugzillas won't---   return all of the results for a very large query by default, but---   you can request this by calling 'searchBugsWithLimit' with 0 for---   the limit.-searchBugsWithLimit :: BugzillaSession-                    -> Int  -- ^ The maximum number of results to return.-                    -> Int  -- ^ The offset from the first result to start from.-                    -> SearchExpression-                    -> IO [Bug]-searchBugsWithLimit session limit offset search = do-  BugList bugs <- doSearchBugs session search Nothing (Just (limit, offset))-  return bugs---- | Similar to 'searchBugsWithLimit', but return _all fields.-searchBugsAllWithLimit :: BugzillaSession-                       -> Int  -- ^ The maximum number of results to return.-                       -> Int  -- ^ The offset from the first result to start from.-                       -> SearchExpression-                       -> IO [Bug]-searchBugsAllWithLimit session limit offset search = do-  BugList bugs <- doSearchBugs session search (Just "_all") (Just (limit, offset))-  return bugs---- | Like 'searchBugsWithLimit', but returns a list of 'BugId's. See--- 'searchBugs'' for more discussion.-searchBugsWithLimit' :: BugzillaSession-                     -> Int  -- ^ The maximum number of results to return.-                     -> Int  -- ^ The offset from the first result to start from.-                     -> SearchExpression-                     -> IO [BugId]-searchBugsWithLimit' session limit offset search = do-  BugIdList bugs <- doSearchBugs session search (Just "id") (Just (limit, offset))-  return bugs---- | Retrieve a bug by bug number.-getBug :: BugzillaSession -> BugId -> IO (Maybe Bug)-getBug session bid = getBugIncludeFields session bid []---- | Retrieve all bug field by bug number-getBugAll :: BugzillaSession -> BugId -> IO (Maybe Bug)-getBugAll session bid = getBugIncludeFields session bid ["_all"]---- | Retrieve a bug by bug number with fields-getBugIncludeFields :: BugzillaSession -> BugId -> [T.Text] -> IO (Maybe Bug)-getBugIncludeFields session bid includeFields = do-  let req = newBzRequest session ["bug", intAsText bid] query-  (BugList bugs) <- sendBzRequest session req-  case bugs of-    [bug] -> return $ Just bug-    []    -> return Nothing-    _     -> throw $ BugzillaUnexpectedValue-                     "Request for a single bug returned multiple bugs"-  where-    query = map (\f -> ("include_fields", Just f)) includeFields---- | Retrieve a bug by attachment number.-getAttachment :: BugzillaSession -> AttachmentId -> IO (Maybe Attachment)-getAttachment session aid = do-  let req = newBzRequest session ["bug", "attachment", intAsText aid] []-  (AttachmentList as) <- sendBzRequest session req-  case as of-    [a] -> return $ Just a-    []  -> return Nothing-    _   -> throw $ BugzillaUnexpectedValue-                   "Request for a single attachment returned multiple attachments"---- | Get all attachments for a bug.-getAttachments :: BugzillaSession -> BugId -> IO [Attachment]-getAttachments session bid = do-  let req = newBzRequest session ["bug", intAsText bid, "attachment"] []-  (AttachmentList as) <- sendBzRequest session req-  return as---- | Get all comments for a bug.-getComments :: BugzillaSession -> BugId -> IO [Comment]-getComments session bid = do-  let req = newBzRequest session ["bug", intAsText bid, "comment"] []-  (CommentList as) <- sendBzRequest session req-  return as---- | Get the history for a bug.-getHistory :: BugzillaSession -> BugId -> IO History-getHistory session bid = do-  let req = newBzRequest session ["bug", intAsText bid, "history"] []-  sendBzRequest session req---- | Search user names and emails using a substring search.-searchUsers :: BugzillaSession -> T.Text -> IO [User]-searchUsers session text = do-  let req = newBzRequest session ["user"] [("match", Just text)]-  (UserList users) <- sendBzRequest session req-  return users---- | Get a user by email.-getUser :: BugzillaSession -> UserEmail -> IO (Maybe User)-getUser session user = do-  let req = newBzRequest session ["user", user] []-  (UserList users) <- sendBzRequest session req-  case users of-    [u] -> return $ Just u-    []  -> return Nothing-    _   -> throw $ BugzillaUnexpectedValue-                   "Request for a single user returned multiple users"---- | Get a user by user ID.-getUserById :: BugzillaSession -> UserId -> IO (Maybe User)-getUserById session uid = do-  let req = newBzRequest session ["user", intAsText uid] []-  (UserList users) <- sendBzRequest session req-  case users of-    [u] -> return $ Just u-    []  -> return Nothing-    _   -> throw $ BugzillaUnexpectedValue-                   "Request for a single user returned multiple users"
− src/Web/Bugzilla/RedHat/Internal/Network.hs
@@ -1,127 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE OverloadedStrings #-}--module Web.Bugzilla.RedHat.Internal.Network-( BugzillaServer-, BugzillaContext (..)-, BugzillaApikey (..)-, BugzillaToken (..)-, BugzillaSession (..)-, BugzillaException (..)-, QueryPart-, Request-, requestUrl-, newBzRequest-, sendBzRequest-) where--import Blaze.ByteString.Builder (toByteString)-#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>))-#endif-import Control.Exception (Exception, throw)-import Control.Monad (mzero)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Resource (runResourceT)-import Data.Aeson-import Data.Maybe (fromMaybe)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-#if !MIN_VERSION_base(4,11,0)-import Data.Monoid ((<>))-#endif-import qualified Data.Text as T-import qualified Data.Text.Encoding as TE-import Data.Typeable-import Network.HTTP.Conduit (Manager, Request(..), Response(..), defaultRequest, host, httpLbs, path, queryString, secure, parseRequest)-import Network.HTTP.Types.URI (QueryText, encodePathSegments, renderQueryText)--type BugzillaServer  = T.Text---- | Holds information about a 'BugzillaServer' and manages outgoing--- connections. You can use 'newBugzillaContext' to create one.-data BugzillaContext = BugzillaContext-  { bzServer  :: BugzillaServer-  , bzManager :: Manager-  }--newtype BugzillaToken = BugzillaToken T.Text--newtype BugzillaApikey = BugzillaApikey T.Text--instance FromJSON BugzillaToken where-  parseJSON (Object v) = BugzillaToken <$> v .: "token"-  parseJSON _          = mzero---- | A session for Bugzilla queries. Use 'anonymousSession' and--- 'loginSession', as appropriate, to create one.-data BugzillaSession = AnonymousSession BugzillaContext-                     | LoginSession BugzillaContext BugzillaToken-                     | ApikeySession BugzillaContext BugzillaApikey--bzContext :: BugzillaSession -> BugzillaContext-bzContext (AnonymousSession ctx) = ctx-bzContext (LoginSession ctx _)   = ctx-bzContext (ApikeySession ctx _)   = ctx--data BugzillaException-  = BugzillaJSONParseError String-  | BugzillaAPIError Int String-  | BugzillaUnexpectedValue String-    deriving (Show, Typeable)-instance Exception BugzillaException--type QueryPart = (T.Text, Maybe T.Text)--requestUrl :: Request -> B.ByteString-requestUrl req = "https://" <> host req <> path req <> queryString req--sslRequest :: Request-sslRequest =-  defaultRequest {-    secure = True,-    port   = 443-  }--newBzRequest :: BugzillaSession -> [T.Text] -> QueryText -> Request-newBzRequest session methodParts query =-    baseRequest {-      path        = toByteString $ encodePathSegments $ "rest" : methodParts,-      queryString = toByteString $ renderQueryText True queryWithToken-    }-  where-    -- Try to parse the bzServer first, if it has a scheme then use it as the base request,-    -- otherwise force a secure ssl request.-    baseRequest :: Request-    baseRequest = fromMaybe (sslRequest { host = serverBytes }) (parseRequest serverStr)-    serverBytes = TE.encodeUtf8 serverTxt-    serverStr = T.unpack serverTxt-    serverTxt = bzServer . bzContext $ session-    queryWithToken = case session of-                       AnonymousSession _                     -> query-                       LoginSession _ (BugzillaToken token)   -> ("token", Just token) : query-                       ApikeySession _ (BugzillaApikey token) -> ("api_key", Just token) : query--data BzError = BzError Int String-               deriving (Eq, Show)--instance FromJSON BzError where-  parseJSON (Object v) = BzError <$> v .: "code"-                                 <*> v .: "message"-  parseJSON _          = mzero--handleError :: String -> BL.ByteString -> IO b-handleError parseError body = do-  let mError = eitherDecode body-  case mError of-    Left _                   -> throw $ BugzillaJSONParseError parseError-    Right (BzError code msg) -> throw $ BugzillaAPIError code msg--sendBzRequest :: FromJSON a => BugzillaSession -> Request -> IO a-sendBzRequest session req = runResourceT $ do-  response <- liftIO $ httpLbs req . bzManager . bzContext $ session-  let mResult = eitherDecode $ responseBody response-  case mResult of-    Left msg      -> liftIO $ handleError msg (responseBody response)-    Right decoded -> return decoded
− src/Web/Bugzilla/RedHat/Internal/Search.hs
@@ -1,90 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-}---- | Operators which can be used to construct queries for Bugzilla.---   These operators are intended to be typesafe: you should not be---   able to construct a query that causes Bugzilla to return an---   error. If you *are* able to construct an erroneous query, please---   report a bug.-module Web.Bugzilla.RedHat.Internal.Search-( FieldType-, SearchTerm (..)-, SearchExpression (..)-, evalSearchExpr-) where--import Data.List-import qualified Data.Text as T-import Data.Time.Clock (UTCTime(..))-import Data.Time.ISO8601 (formatISO8601)--import Web.Bugzilla.RedHat.Internal.Network-import Web.Bugzilla.RedHat.Internal.Types--class FieldType a where fvAsText :: a -> T.Text--instance FieldType T.Text where fvAsText = id-instance FieldType Int where fvAsText = T.pack . show-instance FieldType UTCTime where fvAsText = T.pack . formatISO8601--instance FieldType Bool where-  fvAsText True  = "true"-  fvAsText False = "false"--instance FieldType a => FieldType [a] where-  fvAsText = T.intercalate "," . map fvAsText--data SearchTerm where-  UnaryOp  :: FieldType a => T.Text -> Field a -> SearchTerm-  BinaryOp :: (FieldType a, FieldType b) => T.Text -> Field a -> b -> SearchTerm-  EqTerm   :: (FieldType a, FieldType b) => Field a -> b -> SearchTerm---- | A Boolean expression which can be used to query Bugzilla.-data SearchExpression-  = And [SearchExpression]-  | Or [SearchExpression]-  | Not SearchExpression-  | Term SearchTerm--taggedQueryPart :: Int -> Char -> T.Text -> QueryPart-taggedQueryPart t k v = (T.cons k . T.pack . show $ t, Just v)--termQuery :: FieldType b => Int -> Field a -> T.Text -> b -> [QueryPart]-termQuery t f o v = [taggedQueryPart t 'f' (searchFieldName f),-                     taggedQueryPart t 'o' o,-                     taggedQueryPart t 'v' (fvAsText v)]--evalSearchTerm :: Int -> SearchTerm -> [QueryPart]-evalSearchTerm t (UnaryOp op field)          = termQuery t field op ("" :: T.Text)-evalSearchTerm t (BinaryOp op field val)     = termQuery t field op val-evalSearchTerm _ (EqTerm field val)          = [(searchFieldName field, Just . fvAsText $ val)]--evalSearchExpr :: SearchExpression -> [QueryPart]-evalSearchExpr e = snd $ evalSearchExpr' 1 e-  where-    evalExprGroup :: Int -> [SearchExpression] -> (Int, [QueryPart])-    evalExprGroup t es =-      let (subExprT, subExprQs) = foldl' evalSubExpr (t + 1, []) es-          qs = taggedQueryPart t 'f' "OP" :-               taggedQueryPart subExprT 'f' "CP" :-               subExprQs-      in (subExprT + 1, qs)--    evalSubExpr :: (Int, [QueryPart]) -> SearchExpression -> (Int, [QueryPart])-    evalSubExpr (t, qs) expr = let (nextT, qs') = evalSearchExpr' t expr-                               in  (nextT, qs ++ qs')--    evalSearchExpr' :: Int -> SearchExpression -> (Int, [QueryPart])-    evalSearchExpr' t (And es) = evalExprGroup t es--    evalSearchExpr' t (Or es) =-      let (groupT, groupQs) = evalExprGroup t es-          qs = taggedQueryPart t 'j' "OR" : groupQs-      in (groupT + 1, qs)--    evalSearchExpr' t (Not es) =-      let (groupT, groupQs) = evalSearchExpr' t es-          qs = taggedQueryPart t 'n' "1" : groupQs-      in (groupT + 1, qs)--    evalSearchExpr' t (Term term) = (t + 1, evalSearchTerm t term)
− src/Web/Bugzilla/RedHat/Internal/Types.hs
@@ -1,747 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns #-}--module Web.Bugzilla.RedHat.Internal.Types-( BugId-, AttachmentId-, CommentId-, UserId-, EventId-, FlagId-, FlagType-, UserEmail-, Field (..)-, User (..)-, UserList (..)-, Flag (..)-, ExternalType (..)-, ExternalBug (..)-, Bug (..)-, BugList (..)-, BugIdList (..)-, Attachment (..)-, AttachmentList (..)-, Comment (..)-, CommentList (..)-, History (..)-, HistoryEvent (..)-, Change (..)-, Modification (..)-, fieldName-, searchFieldName-) where--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative (pure, (<$>), (<*>))-#endif-import Control.Monad (mzero)-import Data.Aeson-#if MIN_VERSION_aeson(1,0,0)-import Data.Aeson.Text-#else-import Data.Aeson.Encode-#endif-import Data.Aeson.Types-#if MIN_VERSION_aeson(2,0,0)-import Data.Aeson.Key-import qualified Data.Aeson.KeyMap as M-#else-import qualified Data.HashMap.Strict as M-#endif-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import qualified Data.Text.Lazy.Builder as TLB-import qualified Data.Text.Read as TR-import qualified Data.Vector as V-import Data.Time.Clock (UTCTime(..))--type BugId        = Int-type AttachmentId = Int-type CommentId    = Int-type UserId       = Int-type EventId      = Int-type FlagId       = Int-type FlagType     = Int-type UserEmail    = T.Text---- | A field which you can search by using 'Web.Bugzilla.searchBugs' or track---   changes to using 'Web.Bugzilla.getHistory'. To get a human-readable name for---   a field, use 'fieldName'.-data Field a where-  AliasField                    :: Field [T.Text]         -- Alias-  AssignedToField               :: Field UserEmail        -- Assignee-  AttachmentCreatorField        :: Field UserEmail        -- Attachment creator-  AttachmentDataField           :: Field T.Text           -- Attachment data-  AttachmentDescriptionField    :: Field T.Text           -- Attachment description-  AttachmentFilenameField       :: Field T.Text           -- Attachment filename-  AttachmentIsObsoleteField     :: Field Bool             -- Attachment is obsolete-  AttachmentIsPatchField        :: Field Bool             -- Attachment is patch-  AttachmentIsPrivateField      :: Field Bool             -- Attachment is private-  AttachmentMimetypeField       :: Field T.Text           -- Attachment mime type-  BlocksField                   :: Field Int              -- Blocks-  BugIdField                    :: Field Int              -- Bug ID-  CcField                       :: Field UserEmail        -- CC-  CcListAccessibleField         :: Field Bool             -- CC list accessible-  ClassificationField           :: Field T.Text           -- Classification-  CommentField                  :: Field T.Text           -- Comment-  CommentIsPrivateField         :: Field T.Text           -- Comment is private-  CommentTagsField              :: Field T.Text           -- Comment Tags-  CommenterField                :: Field UserEmail        -- Commenter-  ComponentField                :: Field [T.Text]         -- Component-  ContentField                  :: Field T.Text           -- Content-  CreationDateField             :: Field UTCTime          -- Creation date-  DaysElapsedField              :: Field Int              -- Days since bug changed-  DependsOnField                :: Field Int              -- Depends on-  EverConfirmedField            :: Field Bool             -- Ever confirmed-  FlagRequesteeField            :: Field UserEmail        -- Flag Requestee-  FlagSetterField               :: Field UserEmail        -- Flag Setter-  FlagsField                    :: Field T.Text           -- Flags-  GroupField                    :: Field T.Text           -- Group-  KeywordsField                 :: Field [T.Text]         -- Keywords-  ChangedField                  :: Field UTCTime          -- Changed-  CommentCountField             :: Field Int              -- Number of Comments-  OperatingSystemField          :: Field T.Text           -- OS-  HardwareField                 :: Field T.Text           -- Hardware-  PriorityField                 :: Field T.Text           -- Priority-  ProductField                  :: Field T.Text           -- Product-  QaContactField                :: Field UserEmail        -- QA Contact-  ReporterField                 :: Field UserEmail        -- Reporter-  ReporterAccessibleField       :: Field Bool             -- Reporter accessible-  ResolutionField               :: Field T.Text           -- Resolution-  RestrictCommentsField         :: Field Bool             -- Restrict Comments-  SeeAlsoField                  :: Field T.Text           -- See Also-  SeverityField                 :: Field T.Text           -- Severity-  StatusField                   :: Field T.Text           -- Status-  WhiteboardField               :: Field T.Text           -- Whiteboard-  SummaryField                  :: Field T.Text           -- Summary-  TagsField                     :: Field T.Text           -- Tags-  TargetMilestoneField          :: Field T.Text           -- Target Milestone-  TimeSinceAssigneeTouchedField :: Field Int              -- Time Since Assignee Touched-  BugURLField                   :: Field T.Text           -- URL-  VersionField                  :: Field T.Text           -- Version-  VotesField                    :: Field T.Text           -- Votes-  CustomField                   :: T.Text -> Field T.Text -- (Custom fields)--instance Eq (Field a) where-  (CustomField a) == (CustomField b) = a == b-  (CustomField _) == _               = False-  _ == (CustomField _)               = False-  a == b                             = searchFieldName a == searchFieldName b--instance Show (Field a) where-  show AliasField                    = "AliasField"-  show AssignedToField               = "AssignedToField"-  show AttachmentCreatorField        = "AttachmentCreatorField"-  show AttachmentDataField           = "AttachmentDataField"-  show AttachmentDescriptionField    = "AttachmentDescriptionField"-  show AttachmentFilenameField       = "AttachmentFilenameField"-  show AttachmentIsObsoleteField     = "AttachmentIsObsoleteField"-  show AttachmentIsPatchField        = "AttachmentIsPatchField"-  show AttachmentIsPrivateField      = "AttachmentIsPrivateField"-  show AttachmentMimetypeField       = "AttachmentMimetypeField"-  show BlocksField                   = "BlocksField"-  show BugIdField                    = "BugIdField"-  show CcField                       = "CcField"-  show CcListAccessibleField         = "CcListAccessibleField"-  show ClassificationField           = "ClassificationField"-  show CommentField                  = "CommentField"-  show CommentIsPrivateField         = "CommentIsPrivateField"-  show CommentTagsField              = "CommentTagsField"-  show CommenterField                = "CommenterField"-  show ComponentField                = "ComponentField"-  show ContentField                  = "ContentField"-  show CreationDateField             = "CreationDateField"-  show DaysElapsedField              = "DaysElapsedField"-  show DependsOnField                = "DependsOnField"-  show EverConfirmedField            = "EverConfirmedField"-  show FlagRequesteeField            = "FlagRequesteeField"-  show FlagSetterField               = "FlagSetterField"-  show FlagsField                    = "FlagsField"-  show GroupField                    = "GroupField"-  show KeywordsField                 = "KeywordsField"-  show ChangedField                  = "ChangedField"-  show CommentCountField             = "CommentCountField"-  show OperatingSystemField          = "OperatingSystemField"-  show HardwareField                 = "HardwareField"-  show PriorityField                 = "PriorityField"-  show ProductField                  = "ProductField"-  show QaContactField                = "QaContactField"-  show ReporterField                 = "ReporterField"-  show ReporterAccessibleField       = "ReporterAccessibleField"-  show ResolutionField               = "ResolutionField"-  show RestrictCommentsField         = "RestrictCommentsField"-  show SeeAlsoField                  = "SeeAlsoField"-  show SeverityField                 = "SeverityField"-  show StatusField                   = "StatusField"-  show WhiteboardField               = "WhiteboardField"-  show SummaryField                  = "SummaryField"-  show TagsField                     = "TagsField"-  show TargetMilestoneField          = "TargetMilestoneField"-  show TimeSinceAssigneeTouchedField = "TimeSinceAssigneeTouchedField"-  show BugURLField                   = "BugURLField"-  show VersionField                  = "VersionField"-  show VotesField                    = "VotesField"-  show (CustomField name)            = "CustomField " ++ show name---- | Provides a human-readable name for a 'Field'.-fieldName :: Field a -> T.Text-fieldName AliasField                    = "Alias"-fieldName AssignedToField               = "Assigned to"-fieldName AttachmentCreatorField        = "Attachment creator"-fieldName AttachmentDataField           = "Attachment data"-fieldName AttachmentDescriptionField    = "Attachment description"-fieldName AttachmentFilenameField       = "Attachment filename"-fieldName AttachmentIsObsoleteField     = "Attachment is obsolete"-fieldName AttachmentIsPatchField        = "Attachment is patch"-fieldName AttachmentIsPrivateField      = "Attachment is private"-fieldName AttachmentMimetypeField       = "Attachment MIME type"-fieldName BlocksField                   = "Blocks"-fieldName BugIdField                    = "BugId"-fieldName CcField                       = "CC"-fieldName CcListAccessibleField         = "CC list is accessible"-fieldName ClassificationField           = "Classification"-fieldName CommentField                  = "Comment"-fieldName CommentIsPrivateField         = "Comment is private"-fieldName CommentTagsField              = "Comment tags"-fieldName CommenterField                = "Commenter"-fieldName ComponentField                = "Component"-fieldName ContentField                  = "Content"-fieldName CreationDateField             = "Creation date"-fieldName DaysElapsedField              = "Days elapsed"-fieldName DependsOnField                = "Depends on"-fieldName EverConfirmedField            = "Ever confirmed"-fieldName FlagRequesteeField            = "Flag requestee"-fieldName FlagSetterField               = "Flag setter"-fieldName FlagsField                    = "Flags"-fieldName GroupField                    = "Group"-fieldName KeywordsField                 = "Keywords"-fieldName ChangedField                  = "Changed"-fieldName CommentCountField             = "Comment count"-fieldName OperatingSystemField          = "Operating system"-fieldName HardwareField                 = "Hardware"-fieldName PriorityField                 = "Priority"-fieldName ProductField                  = "Product"-fieldName QaContactField                = "QA contact"-fieldName ReporterField                 = "Reporter"-fieldName ReporterAccessibleField       = "Reporter accessible"-fieldName ResolutionField               = "Resolution"-fieldName RestrictCommentsField         = "Restrict comments"-fieldName SeeAlsoField                  = "See also"-fieldName SeverityField                 = "Severity"-fieldName StatusField                   = "Status"-fieldName WhiteboardField               = "Whiteboard"-fieldName SummaryField                  = "Summary"-fieldName TagsField                     = "Tags"-fieldName TargetMilestoneField          = "Target milestone"-fieldName TimeSinceAssigneeTouchedField = "Time since assignee touched"-fieldName BugURLField                   = "Bug URL"-fieldName VersionField                  = "Version"-fieldName VotesField                    = "Votes"-fieldName (CustomField name)            = T.concat ["Custom field \"", T.pack (show name), "\""]--searchFieldName :: Field a -> T.Text-searchFieldName AliasField                    = "alias"-searchFieldName AssignedToField               = "assigned_to"-searchFieldName AttachmentCreatorField        = "attachments.submitter"-searchFieldName AttachmentDataField           = "attach_data.thedata"-searchFieldName AttachmentDescriptionField    = "attachments.description"-searchFieldName AttachmentFilenameField       = "attachments.filename"-searchFieldName AttachmentIsObsoleteField     = "attachments.isobsolete"-searchFieldName AttachmentIsPatchField        = "attachments.ispatch"-searchFieldName AttachmentIsPrivateField      = "attachments.isprivate"-searchFieldName AttachmentMimetypeField       = "attachments.mimetype"-searchFieldName BlocksField                   = "blocked"-searchFieldName BugIdField                    = "bug_id"-searchFieldName CcField                       = "cc"-searchFieldName CcListAccessibleField         = "cclist_accessible"-searchFieldName ClassificationField           = "classification"-searchFieldName CommentField                  = "longdesc"-searchFieldName CommentIsPrivateField         = "longdescs.isprivate"-searchFieldName CommentTagsField              = "comment_tag"-searchFieldName CommenterField                = "commenter"-searchFieldName ComponentField                = "component"-searchFieldName ContentField                  = "content"-searchFieldName CreationDateField             = "creation_ts"-searchFieldName DaysElapsedField              = "days_elapsed"-searchFieldName DependsOnField                = "dependson"-searchFieldName EverConfirmedField            = "everconfirmed"-searchFieldName FlagRequesteeField            = "requestees.login_name"-searchFieldName FlagSetterField               = "setters.login_name"-searchFieldName FlagsField                    = "flagtypes.name"-searchFieldName GroupField                    = "bug_group"-searchFieldName KeywordsField                 = "keywords"-searchFieldName ChangedField                  = "delta_ts"-searchFieldName CommentCountField             = "longdescs.count"-searchFieldName OperatingSystemField          = "op_sys"-searchFieldName HardwareField                 = "rep_platform"-searchFieldName PriorityField                 = "priority"-searchFieldName ProductField                  = "product"-searchFieldName QaContactField                = "qa_contact"-searchFieldName ReporterField                 = "reporter"-searchFieldName ReporterAccessibleField       = "reporter_accessible"-searchFieldName ResolutionField               = "resolution"-searchFieldName RestrictCommentsField         = "restrict_comments"-searchFieldName SeeAlsoField                  = "see_also"-searchFieldName SeverityField                 = "bug_severity"-searchFieldName StatusField                   = "bug_status"-searchFieldName WhiteboardField               = "status_whiteboard"-searchFieldName SummaryField                  = "short_desc"-searchFieldName TagsField                     = "tag"-searchFieldName TargetMilestoneField          = "target_milestone"-searchFieldName TimeSinceAssigneeTouchedField = "owner_idle_time"-searchFieldName BugURLField                   = "bug_file_loc"-searchFieldName VersionField                  = "version"-searchFieldName VotesField                    = "votes"-searchFieldName (CustomField name)            = name---- | A Bugzilla user.-data User = User-  { userId       :: !UserId-  , userEmail    :: Maybe UserEmail-  , userName     :: T.Text-  , userRealName :: T.Text-  } deriving (Eq, Ord, Show)--instance FromJSON User where-  parseJSON (Object v) =-    User <$> v .: "id"-         <*> v .:? "email"-         <*> v .: "name"-         <*> v .: "real_name"-  parseJSON _ = mzero--newtype UserList = UserList [User]-                deriving (Eq, Show)--instance FromJSON UserList where-  parseJSON (Object v) = UserList <$> v .: "users"-  parseJSON _          = mzero---- | Flags, which may be set on an attachment or on a bug directly.-data Flag = Flag-  { flagId               :: !FlagId-  , flagTypeId           :: !FlagType-  , flagName             :: T.Text-  , flagSetter           :: UserEmail-  , flagStatus           :: T.Text-  , flagCreationDate     :: UTCTime-  , flagModificationDate :: UTCTime-  , flagRequestee        :: Maybe UserEmail-  } deriving (Eq, Ord, Show)--instance FromJSON Flag where-  parseJSON (Object v) =-    Flag <$> v .: "id"-         <*> v .: "type_id"-         <*> v .: "name"-         <*> v .: "setter"-         <*> v .: "status"-         <*> v .: "creation_date"-         <*> v .: "modification_date"-         <*> v .:? "requestee"-  parseJSON _ = mzero---- | An external bug type-data ExternalType = ExternalType-  { externalTypeDescription :: T.Text-  , externalTypeUrl         :: T.Text-  , externalTypeId          :: Int-  , externalTypeType        :: T.Text-  , externalTypeFullUrl     :: T.Text-  } deriving (Eq, Ord, Show)--instance FromJSON ExternalType where-  parseJSON (Object v) =-    ExternalType <$> v .: "description"-                 <*> v .: "url"-                 <*> v .: "id"-                 <*> v .: "type"-                 <*> v .: "full_url"-  parseJSON _ = mzero---- | An external bug.-data ExternalBug = ExternalBug-  { externalDescription    :: T.Text-  , externalBzId           :: Int-  , externalPriority       :: T.Text-  , externalBugId          :: T.Text-  , externalStatus         :: T.Text-  , externalId             :: Int-  , externalType           :: ExternalType-  } deriving (Eq, Ord, Show)--instance FromJSON ExternalBug where-  parseJSON (Object v) =-    ExternalBug <$> v .: "ext_description"-                <*> v .: "ext_bz_id"-                <*> v .: "ext_priority"-                <*> v .: "ext_bz_bug_id"-                <*> v .: "ext_status"-                <*> v .: "id"-                <*> v .: "type"-  parseJSON _ = mzero---- | A Bugzilla bug.-data Bug = Bug-  { bugId                  :: !BugId-  , bugAlias               :: Maybe [T.Text]-  , bugAssignedTo          :: UserEmail-  , bugAssignedToDetail    :: User-  , bugBlocks              :: [BugId]-  , bugCc                  :: [UserEmail]-  , bugCcDetail            :: [User]-  , bugClassification      :: T.Text-  , bugComponent           :: [T.Text]-  , bugCreationTime        :: UTCTime-  , bugCreator             :: UserEmail-  , bugCreatorDetail       :: User-  , bugDependsOn           :: [BugId]-  , bugDupeOf              :: Maybe BugId-  , bugFlags               :: Maybe [Flag]-  , bugGroups              :: [T.Text]-  , bugIsCcAccessible      :: Bool-  , bugIsConfirmed         :: Bool-  , bugIsCreatorAccessible :: Bool-  , bugIsOpen              :: Bool-  , bugKeywords            :: [T.Text]-  , bugLastChangeTime      :: UTCTime-  , bugOpSys               :: T.Text-  , bugPlatform            :: T.Text-  , bugPriority            :: T.Text-  , bugProduct             :: T.Text-  , bugQaContact           :: UserEmail-  , bugResolution          :: T.Text-  , bugSeeAlso             :: [T.Text]-  , bugSeverity            :: T.Text-  , bugStatus              :: T.Text-  , bugSummary             :: T.Text-  , bugTargetMilestone     :: T.Text-  , bugUrl                 :: T.Text-  , bugVersion             :: [T.Text]-  , bugWhiteboard          :: T.Text-  , bugCustomFields        ::-#if MIN_VERSION_aeson(2,0,0)-                              M.KeyMap T.Text-#else-                              M.HashMap T.Text T.Text-#endif-, bugExternalBugs        :: Maybe [ExternalBug]-  } deriving (Eq, Show)--instance FromJSON Bug where-  parseJSON (Object v) =-      Bug <$> v .: "id"-          <*> v .:? "alias"-          <*> v .: "assigned_to"-          <*> v .: "assigned_to_detail"-          <*> v .: "blocks"-          <*> v .: "cc"-          <*> v .: "cc_detail"-          <*> v .: "classification"-          <*> v .: "component"-          <*> v .: "creation_time"-          <*> v .: "creator"-          <*> v .: "creator_detail"-          <*> v .: "depends_on"-          <*> v .:? "dupe_of"-          <*> v .:? "flags"-          <*> v .: "groups"-          <*> v .: "is_cc_accessible"-          <*> v .: "is_confirmed"-          <*> v .: "is_creator_accessible"-          <*> v .: "is_open"-          <*> v .: "keywords"-          <*> v .: "last_change_time"-          <*> v .: "op_sys"-          <*> v .: "platform"-          <*> v .: "priority"-          <*> v .: "product"-          <*> v .: "qa_contact"-          <*> v .: "resolution"-          <*> v .: "see_also"-          <*> v .: "severity"-          <*> v .: "status"-          <*> v .: "summary"-          <*> v .: "target_milestone"-          <*> v .: "url"-          <*> v .: "version"-          <*> v .: "whiteboard"-          <*> pure (customFields v)-          <*> v .:? "external_bugs"-  parseJSON _ = mzero--customFields :: Object ->-#if MIN_VERSION_aeson(2,0,0)-                M.KeyMap T.Text-#else-                M.HashMap T.Text T.Text-#endif-customFields = M.map stringifyCustomFields-             . M.filterWithKey filterCustomFields-  where-    stringifyCustomFields :: Value -> T.Text-    stringifyCustomFields (String t) = t-    stringifyCustomFields v          = T.concat-                                     . TL.toChunks-                                     . TLB.toLazyText-                                     . encodeToTextBuilder-                                     . toJSON-                                     $ v--    filterCustomFields k _ = "cf_" `T.isPrefixOf` toText k-#if !MIN_VERSION_aeson(2,0,0)-      where toText = id-#endif--newtype BugList = BugList [Bug]-               deriving (Eq, Show)--instance FromJSON BugList where-  parseJSON (Object v) = BugList <$> v .: "bugs"-  parseJSON _          = mzero--newtype BugIdList = BugIdList [BugId]-                 deriving (Eq, Show)--instance FromJSON BugIdList where-  parseJSON (Object v) = do-    bugs <- v .: "bugs"-    bugIds <- mapM (.: "id") bugs-    return $ BugIdList bugIds-  parseJSON _          = mzero---- | An attachment to a bug.-data Attachment = Attachment-  { attachmentId             :: !AttachmentId-  , attachmentBugId          :: !BugId-  , attachmentFileName       :: T.Text-  , attachmentSummary        :: T.Text-  , attachmentCreator        :: UserEmail-  , attachmentIsPrivate      :: Bool-  , attachmentIsObsolete     :: Bool-  , attachmentIsPatch        :: Bool-  , attachmentFlags          :: [Flag]-  , attachmentCreationTime   :: UTCTime-  , attachmentLastChangeTime :: UTCTime-  , attachmentContentType    :: T.Text-  , attachmentSize           :: !Int-  , attachmentData           :: T.Text-  } deriving (Eq, Show)--instance FromJSON Attachment where-  parseJSON (Object v) =-    Attachment <$> v .: "id"-               <*> v .: "bug_id"-               <*> v .: "file_name"-               <*> v .: "summary"-               <*> v .: "creator"-               <*> (fromNumericBool <$> v .: "is_private")-               <*> (fromNumericBool <$> v .: "is_obsolete")-               <*> (fromNumericBool <$> v .: "is_patch")-               <*> v .: "flags"-               <*> v .: "creation_time"-               <*> v .: "last_change_time"-               <*> v .: "content_type"-               <*> v .: "size"-               <*> v .: "data"-  parseJSON _ = mzero--fromNumericBool :: Int -> Bool-fromNumericBool 0 = False-fromNumericBool _ = True--newtype AttachmentList = AttachmentList [Attachment]-                      deriving (Eq, Show)--instance FromJSON AttachmentList where-  parseJSON (Object v) = do-    attachmentsVal <- v .: "attachments"-    bugsVal <- v .: "bugs"-    case (attachmentsVal, bugsVal) of-      (Object (M.toList -> [(_, as)]), _) -> AttachmentList . (:[]) <$> parseJSON as-      (_, Object (M.toList -> [(_, as)])) -> AttachmentList <$> parseJSON as-      _                                   -> mzero-  parseJSON _ = mzero---- | A bug comment. To display these the way Bugzilla does, you'll--- need to call 'getUser' and use the 'userRealName' for each user.-data Comment = Comment-  { commentId           :: !CommentId-  , commentBugId        :: !BugId-  , commentAttachmentId :: Maybe AttachmentId-  , commentCount        :: !Int-  , commentText         :: T.Text-  , commentCreator      :: UserEmail-  , commentCreationTime :: UTCTime-  , commentIsPrivate    :: Bool-  } deriving (Eq, Show)--instance FromJSON Comment where-  parseJSON (Object v) =-    Comment <$> v .: "id"-            <*> v .: "bug_id"-            <*> v .: "attachment_id"-            <*> v .: "count"-            <*> v .: "text"-            <*> v .: "creator"-            <*> v .: "creation_time"-            <*> v .: "is_private"-  parseJSON _ = mzero--newtype CommentList = CommentList [Comment]-                   deriving (Eq, Show)--instance FromJSON CommentList where-  parseJSON (Object v) = do-    bugsVal <- v .: "bugs"-    case bugsVal of-      Object (M.toList -> [(_, cs)]) ->-        do comments <- withObject "comments" (.: "comments") cs-           withArray "comment list" (\a -> CommentList <$> parseJSON (addCount a)) comments-      _ -> mzero-  parseJSON _ = mzero---- Note that we make the (possibly unwise) assumption that Bugzilla--- returns the comments in order. If it turns out that's not true, we--- can always sort by their 'id' to ensure correct results.-addCount :: V.Vector Value -> Value-addCount vs = Array $ V.zipWith addCount' (V.enumFromN 0 $ V.length vs) vs- where-   addCount' :: Int -> Value -> Value-   addCount' c (Object v) = Object $ M.insert "count" (Number $ fromIntegral c) v-   addCount' _ v          = v---- | History information for a bug.-data History = History-  { historyBugId   :: !BugId-  , historyEvents  :: [HistoryEvent]-  } deriving (Eq, Show)--instance FromJSON History where-  parseJSON (Object v) = do-    bugsVal <- v .: "bugs"-    case bugsVal of-      Array (V.toList -> [history]) ->-        withObject "history"-                   (\h -> History <$> h .: "id"-                                  <*> parseHistoryEvents h)-                   history-      _ -> mzero-  parseJSON _ = mzero--parseHistoryEvents :: Object -> Parser [HistoryEvent]-parseHistoryEvents h = do-  events <- h .: "history"-  withArray "event list" (parseJSON . addCount) events---- | An event in a bug's history.-data HistoryEvent = HistoryEvent-  { historyEventId      :: EventId   -- ^ A sequential event id.-  , historyEventTime    :: UTCTime   -- ^ When the event occurred.-  , historyEventUser    :: UserEmail -- ^ Which user was responsible.-  , historyEventChanges :: [Change]  -- ^ All the changes which are-                                     --   part of this event.-  } deriving (Eq, Show)--instance FromJSON HistoryEvent where-  parseJSON (Object v) =-    HistoryEvent <$> v .: "count"-                 <*> v .: "when"-                 <*> v .: "who"-                 <*> v .: "changes"-  parseJSON _ = mzero---- | A single change which is part of an event. Different constructors---   are used according to the type of the field. The 'Modification'---   describes the value of the field before and after the change.-data Change-  = TextFieldChange (Field T.Text) (Modification T.Text)-  | ListFieldChange (Field [T.Text]) (Modification [T.Text])-  | IntFieldChange (Field Int) (Modification Int)-  | TimeFieldChange (Field UTCTime) (Modification UTCTime)-  | BoolFieldChange (Field Bool) (Modification Bool)-    deriving (Eq, Show)--instance FromJSON Change where-  parseJSON (Object v) = do-    changedField <- v .: "field_name"-    case changedField of-      "alias"                  -> ListFieldChange AliasField <$> parseModification v-      "assigned_to"            -> TextFieldChange AssignedToField <$> parseModification v-      "attachments.submitter"  -> TextFieldChange AttachmentCreatorField <$> parseModification v-      "attach_data.thedata"    -> TextFieldChange AttachmentDataField <$> parseModification v-      "attachments.description"-> TextFieldChange AttachmentDescriptionField <$> parseModification v-      "attachments.filename"   -> TextFieldChange AttachmentFilenameField <$> parseModification v-      "attachments.isobsolete" -> BoolFieldChange AttachmentIsObsoleteField <$> parseModification v-      "attachments.ispatch"    -> BoolFieldChange AttachmentIsPatchField <$> parseModification v-      "attachments.isprivate"  -> BoolFieldChange AttachmentIsPrivateField <$> parseModification v-      "attachments.mimetype"   -> TextFieldChange AttachmentMimetypeField <$> parseModification v-      "blocks"                 -> IntFieldChange BlocksField <$> parseModification v-      "bug_id"                 -> IntFieldChange BugIdField <$> parseModification v-      "cc"                     -> TextFieldChange CcField <$> parseModification v-      "is_cc_accessible"       -> BoolFieldChange CcListAccessibleField <$> parseModification v-      "classification"         -> TextFieldChange ClassificationField <$> parseModification v-      "component"              -> ListFieldChange ComponentField <$> parseModification v-      "content"                -> TextFieldChange ContentField <$> parseModification v-      "creation_time"          -> TimeFieldChange CreationDateField <$> parseModification v-      "days_elapsed"           -> IntFieldChange DaysElapsedField <$> parseModification v-      "depends_on"             -> IntFieldChange DependsOnField <$> parseModification v-      "everconfirmed"          -> BoolFieldChange EverConfirmedField <$> parseModification v-      "flagtypes.name"         -> TextFieldChange FlagsField <$> parseModification v-      "bug_group"              -> TextFieldChange GroupField <$> parseModification v-      "keywords"               -> ListFieldChange KeywordsField <$> parseModification v-      "op_sys"                 -> TextFieldChange OperatingSystemField <$> parseModification v-      "platform"               -> TextFieldChange HardwareField <$> parseModification v-      "priority"               -> TextFieldChange PriorityField <$> parseModification v-      "product"                -> TextFieldChange ProductField <$> parseModification v-      "qa_contact"             -> TextFieldChange QaContactField <$> parseModification v-      "reporter"               -> TextFieldChange ReporterField <$> parseModification v-      "reporter_accessible"    -> BoolFieldChange ReporterAccessibleField <$> parseModification v-      "resolution"             -> TextFieldChange ResolutionField <$> parseModification v-      "restrict_comments"      -> BoolFieldChange RestrictCommentsField <$> parseModification v-      "see_also"               -> TextFieldChange SeeAlsoField <$> parseModification v-      "severity"               -> TextFieldChange SeverityField <$> parseModification v-      "status"                 -> TextFieldChange StatusField <$> parseModification v-      "whiteboard"             -> TextFieldChange WhiteboardField <$> parseModification v-      "summary"                -> TextFieldChange SummaryField <$> parseModification v-      "tag"                    -> TextFieldChange TagsField <$> parseModification v-      "target_milestone"       -> TextFieldChange TargetMilestoneField <$> parseModification v-      "url"                    -> TextFieldChange BugURLField <$> parseModification v-      "version"                -> TextFieldChange VersionField <$> parseModification v-      "votes"                  -> TextFieldChange VotesField <$> parseModification v-      name                     -> TextFieldChange (CustomField name) <$> parseModification v-  parseJSON _ = mzero---- | A description of how a field changed during a 'HistoryEvent'.-data (Eq a, Show a) => Modification a = Modification-  { modRemoved      :: Maybe a-  , modAdded        :: Maybe a-  , modAttachmentId :: Maybe AttachmentId-  } deriving (Eq, Show)--parseModification :: (FromJSON a, Eq b, Show b, ToModification a b) => Object -> Parser (Modification b)-parseModification v = Modification <$> (toMod =<< v .: "removed")-                                   <*> (toMod =<< v .: "added")-                                   <*> v .:? "attachment_id"--class ToModification a b | b -> a where toMod :: a -> Parser (Maybe b)-instance ToModification T.Text T.Text where toMod = return . Just-instance ToModification UTCTime UTCTime where toMod = return . Just--instance ToModification T.Text Int where-  toMod v | v == "" = return Nothing-          | otherwise = case TR.decimal v of-                          Left _       -> mzero-                          Right (i, _) -> return $ Just i--instance ToModification T.Text Bool where-  toMod v | v == "0"  = return $ Just False-          | v == "1"  = return $ Just True-          | otherwise = mzero--instance ToModification T.Text [T.Text] where-  toMod v = return . Just $ T.splitOn ", " v
− src/Web/Bugzilla/RedHat/Search.hs
@@ -1,147 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-}---- | A modified version of Web.Bugzilla.Search to support---   the list fields in Red Hat's modified bugzilla API.--module Web.Bugzilla.RedHat.Search-(-  -- * Search operators-  (.==.)-, (./=.)-, (.<.)-, (.<=.)-, (.>.)-, (.>=.)-, (.=~.)-, (./=~.)-, equalsAny-, contains-, containsCase-, containsAny-, containsAll-, changedBefore-, changedAfter-, changedSince-, changedUntil-, changedRange-, changedFrom-, changedTo-, changedBy-, contentMatches-, isEmpty-, isNotEmpty-, (.&&.)-, (.||.)-, not'--  -- * Search expressions-, Field (..)-, SearchExpression-, evalSearchExpr-) where--import qualified Data.Text as T-import Data.Time.Clock (UTCTime(..))--import Web.Bugzilla.RedHat.Internal.Search-import Web.Bugzilla.RedHat.Internal.Types--(.==.) :: FieldType a => Field a -> a -> SearchExpression-(.==.) = (Term .) . BinaryOp "equals"-infix 4 .==.--(./=.) :: FieldType a => Field a -> a -> SearchExpression-(./=.) = (Term .) . BinaryOp "notequals"-infix 4 ./=.--(.<.) :: FieldType a => Field a -> a -> SearchExpression-(.<.) = (Term .) . BinaryOp "lessthan"-infix 4 .<.--(.<=.) :: FieldType a => Field a -> a -> SearchExpression-(.<=.) = (Term .) . BinaryOp "lessthaneq"-infix 4 .<=.--(.>.) :: FieldType a => Field a -> a -> SearchExpression-(.>.) = (Term .) . BinaryOp "greaterthan"-infix 4 .>.--(.>=.) :: FieldType a => Field a -> a -> SearchExpression-(.>=.) = (Term .) . BinaryOp "greaterthaneq"-infix 4 .>=.--(.=~.) :: FieldType a => Field a -> a -> SearchExpression-(.=~.) = (Term .) . BinaryOp "regexp"--(./=~.) :: FieldType a => Field a -> a -> SearchExpression-(./=~.) = (Term .) . BinaryOp "notregexp"--equalsAny :: FieldType a => Field a -> [a] -> SearchExpression-equalsAny = (Term .) . BinaryOp "anyexact"--contains :: Field T.Text -> T.Text -> SearchExpression-contains = (Term .) . BinaryOp "substring"--containsCase :: Field T.Text -> T.Text -> SearchExpression-containsCase = (Term .) . BinaryOp "casesubstring"--containsAny :: Field T.Text -> [T.Text] -> SearchExpression-containsAny = (Term .) . BinaryOp "anywordssubstr"--containsAll :: Field T.Text -> [T.Text] -> SearchExpression-containsAll = (Term .) . BinaryOp "allwordssubstr"--changedBefore :: FieldType a => Field a -> UTCTime -> SearchExpression-changedBefore = (Term .) . BinaryOp "changedbefore"--changedAfter :: FieldType a => Field a -> UTCTime -> SearchExpression-changedAfter = (Term .) . BinaryOp "changedafter"---- | Filter bug changed since UTCTime-changedSince :: UTCTime -> SearchExpression-changedSince ts = Term $ EqTerm (CustomField "chfieldfrom") ts---- | Filter bug changed until UTCTime-changedUntil :: UTCTime -> SearchExpression-changedUntil ts = Term $ EqTerm (CustomField "chfieldto") ts---- | Filter bug changed in range-changedRange :: UTCTime -> UTCTime -> SearchExpression-changedRange from to = changedSince from .&&. changedUntil to--changedFrom :: FieldType a => Field a -> a -> SearchExpression-changedFrom = (Term .) . BinaryOp "changedfrom"--changedTo :: FieldType a => Field a -> a -> SearchExpression-changedTo = (Term .) . BinaryOp "changedto"--changedBy :: FieldType a => Field a -> UserEmail -> SearchExpression-changedBy = (Term .) . BinaryOp "changedby"--contentMatches :: T.Text -> SearchExpression-contentMatches = Term . BinaryOp "matches" ContentField--isEmpty :: FieldType a => Field a -> SearchExpression-isEmpty = Term . UnaryOp "isempty"--isNotEmpty :: FieldType a => Field a -> SearchExpression-isNotEmpty = Term . UnaryOp "isnotempty"--(.&&.) :: SearchExpression -> SearchExpression -> SearchExpression-(.&&.) (And as) (And bs) = And (as ++ bs)-(.&&.) (And as) a = And (as ++ [a])-(.&&.) a (And as) = And (a:as)-(.&&.) a b        = And [a, b]-infixr 3 .&&.--(.||.) :: SearchExpression -> SearchExpression -> SearchExpression-(.||.) (Or as) (Or bs) = Or (as ++ bs)-(.||.) a (Or as) = Or (a:as)-(.||.) (Or as) a = Or (as ++ [a])-(.||.) a b       = Or [a, b]-infixr 2 .||.--not' :: SearchExpression -> SearchExpression-not' (Not a) = a-not' a       = Not a
+ src/Web/RedHatBugzilla.hs view
@@ -0,0 +1,249 @@+{-# LANGUAGE OverloadedStrings #-}++-- | This package is designed to provide an easy-to-use, typesafe+--   interface to querying Bugzilla from Haskell.+--+--   A modified version of Web.Bugzilla to support+--   the list fields in Red Hat's modified bugzilla API.+--+--   A very simple program using this package might look like this:+--+-- >   let session = anonymousSession "https://bugzilla.redhat.com"+-- >       user = "me@example.org"+-- >       query = AssignedToField .==. user .&&.+-- >               FlagRequesteeField .==. user .&&.+-- >               (FlagsField `contains` "review" .||. FlagsField `contains` "feedback")+-- >   bugs <- searchBugs session query+-- >   mapM_ (putStrLn . show . bugSummary) bugs+--+--   There's a more in-depth demo program included with the+--   source code to this package.++module Web.RedHatBugzilla+( -- * Connecting to Bugzilla+  apikeySession+, anonymousSession++, BugzillaServer+, BugzillaSession (..)+, BugzillaApiKey (..)++  -- * Querying Bugzilla+, searchBugs+, searchBugsAll+, searchBugs'+, searchBugsWithLimit+, searchBugsAllWithLimit+, searchBugsWithLimit'+, getBug+, getBugAll+, getAttachment+, getAttachments+, getComments+, getHistory+, searchUsers+, getUser+, getUserById+, newBzRequest+, sendBzRequest+, intAsText++, Request+, BugId+, AttachmentId+, CommentId+, UserId+, EventId+, FlagId+, FlagType+, UserEmail+, Field (..)+, User (..)+, Flag (..)+, Bug (..)+, ExternalBug (..)+, ExternalType (..)+, Attachment (..)+, Comment (..)+, History (..)+, HistoryEvent (..)+, Change (..)+, Modification (..)+, fieldName++, BugzillaException (..)+) where++import Control.Exception (throw)+import Data.Aeson (FromJSON)+import qualified Data.Text as T++import Web.RedHatBugzilla.Internal.Network+import Web.RedHatBugzilla.Internal.Search+import Web.RedHatBugzilla.Internal.Types++-- | Creates a 'BugzillaSession' using the provided api key.+apikeySession :: BugzillaServer -> BugzillaApiKey -> BugzillaSession+apikeySession = ApiKeySession++-- | Creates an anonymous 'BugzillaSession'. Note that some content+--   will be hidden by Bugzilla when you make queries in this state.+anonymousSession :: BugzillaServer -> BugzillaSession+anonymousSession = AnonymousSession++intAsText :: Int -> T.Text+intAsText = T.pack . show++-- | Searches Bugzilla and returns a list of 'Bug's. The 'SearchExpression'+-- can be constructed conveniently using the operators in "Web.Bugzilla.Search".+searchBugs :: BugzillaSession -> SearchExpression -> IO [Bug]+searchBugs session search = do+  BugList bugs <- doSearchBugs session search Nothing Nothing+  return bugs++-- | Similar to 'searchBugs', but return _all fields.+searchBugsAll :: BugzillaSession -> SearchExpression -> IO [Bug]+searchBugsAll session search = do+  BugList bugs <- doSearchBugs session search (Just "_all") Nothing+  return bugs++-- | Like 'searchBugs', but returns a list of 'BugId's. You can+-- retrieve the 'Bug' for each 'BugId' using 'getBug'. The combination+-- of 'searchBugs'' and 'getBug' is much less efficient than+-- 'searchBugs'. 'searchBugs'' is suitable for cases where you won't need to call+-- 'getBug' most of the time - for example, polling to determine whether the+-- set of bugs returned by a query has changed.+searchBugs' :: BugzillaSession -> SearchExpression -> IO [BugId]+searchBugs' session search = do+  BugIdList bugs <- doSearchBugs session search (Just "id") Nothing+  return bugs++doSearchBugs :: FromJSON a => BugzillaSession -> SearchExpression -> Maybe T.Text -> Maybe (Int, Int) -> IO a+doSearchBugs session search includeField limits = do+  let fieldsQuery = case includeField of+        Nothing -> []+        Just field -> [("include_fields", Just field)]+      limitQuery = case limits of+        Nothing -> []+        Just (limit, offset) -> [("limit", Just $ intAsText limit),+                                 ("offset", Just $ intAsText offset)]+      searchQuery = evalSearchExpr search+      req = newBzRequest session ["bug"] (limitQuery ++ fieldsQuery ++ searchQuery)+  sendBzRequest req++-- | Search Bugzilla and returns a limited number of results. You can+--   call this repeatedly and use 'offset' to retrieve the results of+--   a large query incrementally. Note that most Bugzillas won't+--   return all of the results for a very large query by default, but+--   you can request this by calling 'searchBugsWithLimit' with 0 for+--   the limit.+searchBugsWithLimit :: BugzillaSession+                    -> Int  -- ^ The maximum number of results to return.+                    -> Int  -- ^ The offset from the first result to start from.+                    -> SearchExpression+                    -> IO [Bug]+searchBugsWithLimit session limit offset search = do+  BugList bugs <- doSearchBugs session search Nothing (Just (limit, offset))+  return bugs++-- | Similar to 'searchBugsWithLimit', but return _all fields.+searchBugsAllWithLimit :: BugzillaSession+                       -> Int  -- ^ The maximum number of results to return.+                       -> Int  -- ^ The offset from the first result to start from.+                       -> SearchExpression+                       -> IO [Bug]+searchBugsAllWithLimit session limit offset search = do+  BugList bugs <- doSearchBugs session search (Just "_all") (Just (limit, offset))+  return bugs++-- | Like 'searchBugsWithLimit', but returns a list of 'BugId's. See+-- 'searchBugs'' for more discussion.+searchBugsWithLimit' :: BugzillaSession+                     -> Int  -- ^ The maximum number of results to return.+                     -> Int  -- ^ The offset from the first result to start from.+                     -> SearchExpression+                     -> IO [BugId]+searchBugsWithLimit' session limit offset search = do+  BugIdList bugs <- doSearchBugs session search (Just "id") (Just (limit, offset))+  return bugs++-- | Retrieve a bug by bug number.+getBug :: BugzillaSession -> BugId -> IO (Maybe Bug)+getBug session bid = getBugIncludeFields session bid []++-- | Retrieve all bug field by bug number+getBugAll :: BugzillaSession -> BugId -> IO (Maybe Bug)+getBugAll session bid = getBugIncludeFields session bid ["_all"]++-- | Retrieve a bug by bug number with fields+getBugIncludeFields :: BugzillaSession -> BugId -> [T.Text] -> IO (Maybe Bug)+getBugIncludeFields session bid includeFields = do+  let req = newBzRequest session ["bug", intAsText bid] query+  (BugList bugs) <- sendBzRequest req+  case bugs of+    [bug] -> return $ Just bug+    []    -> return Nothing+    _     -> throw $ BugzillaUnexpectedValue+                     "Request for a single bug returned multiple bugs"+  where+    query = map (\f -> ("include_fields", Just f)) includeFields++-- | Retrieve a bug by attachment number.+getAttachment :: BugzillaSession -> AttachmentId -> IO (Maybe Attachment)+getAttachment session aid = do+  let req = newBzRequest session ["bug", "attachment", intAsText aid] []+  (AttachmentList as) <- sendBzRequest req+  case as of+    [a] -> return $ Just a+    []  -> return Nothing+    _   -> throw $ BugzillaUnexpectedValue+                   "Request for a single attachment returned multiple attachments"++-- | Get all attachments for a bug.+getAttachments :: BugzillaSession -> BugId -> IO [Attachment]+getAttachments session bid = do+  let req = newBzRequest session ["bug", intAsText bid, "attachment"] []+  (AttachmentList as) <- sendBzRequest req+  return as++-- | Get all comments for a bug.+getComments :: BugzillaSession -> BugId -> IO [Comment]+getComments session bid = do+  let req = newBzRequest session ["bug", intAsText bid, "comment"] []+  (CommentList as) <- sendBzRequest req+  return as++-- | Get the history for a bug.+getHistory :: BugzillaSession -> BugId -> IO History+getHistory session bid = do+  let req = newBzRequest session ["bug", intAsText bid, "history"] []+  sendBzRequest req++-- | Search user names and emails using a substring search.+searchUsers :: BugzillaSession -> T.Text -> IO [User]+searchUsers session text = do+  let req = newBzRequest session ["user"] [("match", Just text)]+  (UserList users) <- sendBzRequest req+  return users++-- | Get a user by email.+getUser :: BugzillaSession -> UserEmail -> IO (Maybe User)+getUser session user = do+  let req = newBzRequest session ["user", user] []+  (UserList users) <- sendBzRequest req+  case users of+    [u] -> return $ Just u+    []  -> return Nothing+    _   -> throw $ BugzillaUnexpectedValue+                   "Request for a single user returned multiple users"++-- | Get a user by user ID.+getUserById :: BugzillaSession -> UserId -> IO (Maybe User)+getUserById session uid = do+  let req = newBzRequest session ["user", intAsText uid] []+  (UserList users) <- sendBzRequest req+  case users of+    [u] -> return $ Just u+    []  -> return Nothing+    _   -> throw $ BugzillaUnexpectedValue+                   "Request for a single user returned multiple users"
+ src/Web/RedHatBugzilla/Internal/Network.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++module Web.RedHatBugzilla.Internal.Network+( BugzillaServer+, BugzillaApiKey (..)+, BugzillaSession (..)+, BugzillaException (..)+, QueryPart+, Request+, requestUrl+, newBzRequest+, sendBzRequest+) where++import Blaze.ByteString.Builder (toByteString)+#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>), (<*>))+#endif+import Control.Exception (Exception, throw)+import Control.Monad (mzero)+import Control.Monad.IO.Class (liftIO)+import Control.Monad.Trans.Resource (runResourceT)+import Data.Aeson+import Data.Maybe (fromMaybe)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid ((<>))+#endif+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import Data.Typeable+import Network.HTTP.Simple (defaultRequest, httpLBS, parseRequest)+import Network.HTTP.Conduit (Request(..), Response(..), host, path, port,+                             queryString, requestHeaders, secure)+import Network.HTTP.Types.URI (QueryText, encodePathSegments, renderQueryText)++type BugzillaServer  = T.Text++newtype BugzillaApiKey = BugzillaApiKey T.Text++-- | A session for Bugzilla queries. Use 'anonymousSession' and+-- 'loginSession', as appropriate, to create one.+data BugzillaSession = AnonymousSession BugzillaServer+                     | ApiKeySession BugzillaServer BugzillaApiKey++bzServer :: BugzillaSession -> BugzillaServer+bzServer (AnonymousSession svr) = svr+bzServer (ApiKeySession svr _)   = svr++data BugzillaException+  = BugzillaJSONParseError String+  | BugzillaAPIError Int String+  | BugzillaUnexpectedValue String+  deriving (Show, Typeable)++instance Exception BugzillaException++type QueryPart = (T.Text, Maybe T.Text)++requestUrl :: Request -> B.ByteString+requestUrl req = "https://" <> host req <> path req <> queryString req++sslRequest :: Request+sslRequest =+  defaultRequest {+    secure = True,+    port   = 443+  }++newBzRequest :: BugzillaSession -> [T.Text] -> QueryText -> Request+newBzRequest session methodParts query =+    let req =+          baseRequest {+          path = toByteString $ encodePathSegments $ "rest" : methodParts,+          queryString = toByteString $ renderQueryText True query+          }+    in case session of+         ApiKeySession _ (BugzillaApiKey key) ->+           req { requestHeaders = [("Authorization",+                                    "Bearer " <> TE.encodeUtf8 key)] }+         _ -> req+  where+    -- Try to parse the bzServer first, if it has a scheme then use it as the base request,+    -- otherwise force a secure ssl request.+    baseRequest :: Request+    baseRequest = fromMaybe (sslRequest { host = serverBytes }) (parseRequest serverStr)+    serverBytes = TE.encodeUtf8 serverTxt+    serverStr = T.unpack serverTxt+    serverTxt = bzServer session++data BzError = BzError Int String+               deriving (Eq, Show)++instance FromJSON BzError where+  parseJSON (Object v) = BzError <$> v .: "code"+                                 <*> v .: "message"+  parseJSON _          = mzero++handleError :: String -> BL.ByteString -> IO b+handleError parseError body = do+  let mError = eitherDecode body+  case mError of+    Left _                   -> throw $ BugzillaJSONParseError parseError+    Right (BzError code msg) -> throw $ BugzillaAPIError code msg++sendBzRequest :: FromJSON a => Request -> IO a+sendBzRequest req = runResourceT $ do+  response <- liftIO $ httpLBS req+  let mResult = eitherDecode $ responseBody response+  case mResult of+    Left msg      -> liftIO $ handleError msg (responseBody response)+    Right decoded -> return decoded
+ src/Web/RedHatBugzilla/Internal/Search.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Operators which can be used to construct queries for Bugzilla.+--   These operators are intended to be typesafe: you should not be+--   able to construct a query that causes Bugzilla to return an+--   error. If you *are* able to construct an erroneous query, please+--   report a bug.+module Web.RedHatBugzilla.Internal.Search+( FieldType+, SearchTerm (..)+, SearchExpression (..)+, evalSearchExpr+) where++import Data.List+import qualified Data.Text as T+import Data.Time.Clock (UTCTime(..))+import Data.Time.ISO8601 (formatISO8601)++import Web.RedHatBugzilla.Internal.Network+import Web.RedHatBugzilla.Internal.Types++class FieldType a where fvAsText :: a -> T.Text++instance FieldType T.Text where fvAsText = id+instance FieldType Int where fvAsText = T.pack . show+instance FieldType UTCTime where fvAsText = T.pack . formatISO8601++instance FieldType Bool where+  fvAsText True  = "true"+  fvAsText False = "false"++instance FieldType a => FieldType [a] where+  fvAsText = T.intercalate "," . map fvAsText++data SearchTerm where+  UnaryOp  :: FieldType a => T.Text -> Field a -> SearchTerm+  BinaryOp :: (FieldType a, FieldType b) => T.Text -> Field a -> b -> SearchTerm+  EqTerm   :: (FieldType a, FieldType b) => Field a -> b -> SearchTerm++-- | A Boolean expression which can be used to query Bugzilla.+data SearchExpression+  = And [SearchExpression]+  | Or [SearchExpression]+  | Not SearchExpression+  | Term SearchTerm++taggedQueryPart :: Int -> Char -> T.Text -> QueryPart+taggedQueryPart t k v = (T.cons k . T.pack . show $ t, Just v)++termQuery :: FieldType b => Int -> Field a -> T.Text -> b -> [QueryPart]+termQuery t f o v = [taggedQueryPart t 'f' (searchFieldName f),+                     taggedQueryPart t 'o' o,+                     taggedQueryPart t 'v' (fvAsText v)]++evalSearchTerm :: Int -> SearchTerm -> [QueryPart]+evalSearchTerm t (UnaryOp op field)          = termQuery t field op ("" :: T.Text)+evalSearchTerm t (BinaryOp op field val)     = termQuery t field op val+evalSearchTerm _ (EqTerm field val)          = [(searchFieldName field, Just . fvAsText $ val)]++evalSearchExpr :: SearchExpression -> [QueryPart]+evalSearchExpr e = snd $ evalSearchExpr' 1 e+  where+    evalExprGroup :: Int -> [SearchExpression] -> (Int, [QueryPart])+    evalExprGroup t es =+      let (subExprT, subExprQs) = foldl' evalSubExpr (t + 1, []) es+          qs = taggedQueryPart t 'f' "OP" :+               taggedQueryPart subExprT 'f' "CP" :+               subExprQs+      in (subExprT + 1, qs)++    evalSubExpr :: (Int, [QueryPart]) -> SearchExpression -> (Int, [QueryPart])+    evalSubExpr (t, qs) expr = let (nextT, qs') = evalSearchExpr' t expr+                               in  (nextT, qs ++ qs')++    evalSearchExpr' :: Int -> SearchExpression -> (Int, [QueryPart])+    evalSearchExpr' t (And es) = evalExprGroup t es++    evalSearchExpr' t (Or es) =+      let (groupT, groupQs) = evalExprGroup t es+          qs = taggedQueryPart t 'j' "OR" : groupQs+      in (groupT + 1, qs)++    evalSearchExpr' t (Not es) =+      let (groupT, groupQs) = evalSearchExpr' t es+          qs = taggedQueryPart t 'n' "1" : groupQs+      in (groupT + 1, qs)++    evalSearchExpr' t (Term term) = (t + 1, evalSearchTerm t term)
+ src/Web/RedHatBugzilla/Internal/Types.hs view
@@ -0,0 +1,747 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-}++module Web.RedHatBugzilla.Internal.Types+( BugId+, AttachmentId+, CommentId+, UserId+, EventId+, FlagId+, FlagType+, UserEmail+, Field (..)+, User (..)+, UserList (..)+, Flag (..)+, ExternalType (..)+, ExternalBug (..)+, Bug (..)+, BugList (..)+, BugIdList (..)+, Attachment (..)+, AttachmentList (..)+, Comment (..)+, CommentList (..)+, History (..)+, HistoryEvent (..)+, Change (..)+, Modification (..)+, fieldName+, searchFieldName+) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative (pure, (<$>), (<*>))+#endif+import Control.Monad (mzero)+import Data.Aeson+#if MIN_VERSION_aeson(1,0,0)+import Data.Aeson.Text+#else+import Data.Aeson.Encode+#endif+import Data.Aeson.Types+#if MIN_VERSION_aeson(2,0,0)+import Data.Aeson.Key+import qualified Data.Aeson.KeyMap as M+#else+import qualified Data.HashMap.Strict as M+#endif+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TLB+import qualified Data.Text.Read as TR+import qualified Data.Vector as V+import Data.Time.Clock (UTCTime(..))++type BugId        = Int+type AttachmentId = Int+type CommentId    = Int+type UserId       = Int+type EventId      = Int+type FlagId       = Int+type FlagType     = Int+type UserEmail    = T.Text++-- | A field which you can search by using 'Web.Bugzilla.searchBugs' or track+--   changes to using 'Web.Bugzilla.getHistory'. To get a human-readable name for+--   a field, use 'fieldName'.+data Field a where+  AliasField                    :: Field [T.Text]         -- Alias+  AssignedToField               :: Field UserEmail        -- Assignee+  AttachmentCreatorField        :: Field UserEmail        -- Attachment creator+  AttachmentDataField           :: Field T.Text           -- Attachment data+  AttachmentDescriptionField    :: Field T.Text           -- Attachment description+  AttachmentFilenameField       :: Field T.Text           -- Attachment filename+  AttachmentIsObsoleteField     :: Field Bool             -- Attachment is obsolete+  AttachmentIsPatchField        :: Field Bool             -- Attachment is patch+  AttachmentIsPrivateField      :: Field Bool             -- Attachment is private+  AttachmentMimetypeField       :: Field T.Text           -- Attachment mime type+  BlocksField                   :: Field Int              -- Blocks+  BugIdField                    :: Field Int              -- Bug ID+  CcField                       :: Field UserEmail        -- CC+  CcListAccessibleField         :: Field Bool             -- CC list accessible+  ClassificationField           :: Field T.Text           -- Classification+  CommentField                  :: Field T.Text           -- Comment+  CommentIsPrivateField         :: Field T.Text           -- Comment is private+  CommentTagsField              :: Field T.Text           -- Comment Tags+  CommenterField                :: Field UserEmail        -- Commenter+  ComponentField                :: Field [T.Text]         -- Component+  ContentField                  :: Field T.Text           -- Content+  CreationDateField             :: Field UTCTime          -- Creation date+  DaysElapsedField              :: Field Int              -- Days since bug changed+  DependsOnField                :: Field Int              -- Depends on+  EverConfirmedField            :: Field Bool             -- Ever confirmed+  FlagRequesteeField            :: Field UserEmail        -- Flag Requestee+  FlagSetterField               :: Field UserEmail        -- Flag Setter+  FlagsField                    :: Field T.Text           -- Flags+  GroupField                    :: Field T.Text           -- Group+  KeywordsField                 :: Field [T.Text]         -- Keywords+  ChangedField                  :: Field UTCTime          -- Changed+  CommentCountField             :: Field Int              -- Number of Comments+  OperatingSystemField          :: Field T.Text           -- OS+  HardwareField                 :: Field T.Text           -- Hardware+  PriorityField                 :: Field T.Text           -- Priority+  ProductField                  :: Field T.Text           -- Product+  QaContactField                :: Field UserEmail        -- QA Contact+  ReporterField                 :: Field UserEmail        -- Reporter+  ReporterAccessibleField       :: Field Bool             -- Reporter accessible+  ResolutionField               :: Field T.Text           -- Resolution+  RestrictCommentsField         :: Field Bool             -- Restrict Comments+  SeeAlsoField                  :: Field T.Text           -- See Also+  SeverityField                 :: Field T.Text           -- Severity+  StatusField                   :: Field T.Text           -- Status+  WhiteboardField               :: Field T.Text           -- Whiteboard+  SummaryField                  :: Field T.Text           -- Summary+  TagsField                     :: Field T.Text           -- Tags+  TargetMilestoneField          :: Field T.Text           -- Target Milestone+  TimeSinceAssigneeTouchedField :: Field Int              -- Time Since Assignee Touched+  BugURLField                   :: Field T.Text           -- URL+  VersionField                  :: Field T.Text           -- Version+  VotesField                    :: Field T.Text           -- Votes+  CustomField                   :: T.Text -> Field T.Text -- (Custom fields)++instance Eq (Field a) where+  (CustomField a) == (CustomField b) = a == b+  (CustomField _) == _               = False+  _ == (CustomField _)               = False+  a == b                             = searchFieldName a == searchFieldName b++instance Show (Field a) where+  show AliasField                    = "AliasField"+  show AssignedToField               = "AssignedToField"+  show AttachmentCreatorField        = "AttachmentCreatorField"+  show AttachmentDataField           = "AttachmentDataField"+  show AttachmentDescriptionField    = "AttachmentDescriptionField"+  show AttachmentFilenameField       = "AttachmentFilenameField"+  show AttachmentIsObsoleteField     = "AttachmentIsObsoleteField"+  show AttachmentIsPatchField        = "AttachmentIsPatchField"+  show AttachmentIsPrivateField      = "AttachmentIsPrivateField"+  show AttachmentMimetypeField       = "AttachmentMimetypeField"+  show BlocksField                   = "BlocksField"+  show BugIdField                    = "BugIdField"+  show CcField                       = "CcField"+  show CcListAccessibleField         = "CcListAccessibleField"+  show ClassificationField           = "ClassificationField"+  show CommentField                  = "CommentField"+  show CommentIsPrivateField         = "CommentIsPrivateField"+  show CommentTagsField              = "CommentTagsField"+  show CommenterField                = "CommenterField"+  show ComponentField                = "ComponentField"+  show ContentField                  = "ContentField"+  show CreationDateField             = "CreationDateField"+  show DaysElapsedField              = "DaysElapsedField"+  show DependsOnField                = "DependsOnField"+  show EverConfirmedField            = "EverConfirmedField"+  show FlagRequesteeField            = "FlagRequesteeField"+  show FlagSetterField               = "FlagSetterField"+  show FlagsField                    = "FlagsField"+  show GroupField                    = "GroupField"+  show KeywordsField                 = "KeywordsField"+  show ChangedField                  = "ChangedField"+  show CommentCountField             = "CommentCountField"+  show OperatingSystemField          = "OperatingSystemField"+  show HardwareField                 = "HardwareField"+  show PriorityField                 = "PriorityField"+  show ProductField                  = "ProductField"+  show QaContactField                = "QaContactField"+  show ReporterField                 = "ReporterField"+  show ReporterAccessibleField       = "ReporterAccessibleField"+  show ResolutionField               = "ResolutionField"+  show RestrictCommentsField         = "RestrictCommentsField"+  show SeeAlsoField                  = "SeeAlsoField"+  show SeverityField                 = "SeverityField"+  show StatusField                   = "StatusField"+  show WhiteboardField               = "WhiteboardField"+  show SummaryField                  = "SummaryField"+  show TagsField                     = "TagsField"+  show TargetMilestoneField          = "TargetMilestoneField"+  show TimeSinceAssigneeTouchedField = "TimeSinceAssigneeTouchedField"+  show BugURLField                   = "BugURLField"+  show VersionField                  = "VersionField"+  show VotesField                    = "VotesField"+  show (CustomField name)            = "CustomField " ++ show name++-- | Provides a human-readable name for a 'Field'.+fieldName :: Field a -> T.Text+fieldName AliasField                    = "Alias"+fieldName AssignedToField               = "Assigned to"+fieldName AttachmentCreatorField        = "Attachment creator"+fieldName AttachmentDataField           = "Attachment data"+fieldName AttachmentDescriptionField    = "Attachment description"+fieldName AttachmentFilenameField       = "Attachment filename"+fieldName AttachmentIsObsoleteField     = "Attachment is obsolete"+fieldName AttachmentIsPatchField        = "Attachment is patch"+fieldName AttachmentIsPrivateField      = "Attachment is private"+fieldName AttachmentMimetypeField       = "Attachment MIME type"+fieldName BlocksField                   = "Blocks"+fieldName BugIdField                    = "BugId"+fieldName CcField                       = "CC"+fieldName CcListAccessibleField         = "CC list is accessible"+fieldName ClassificationField           = "Classification"+fieldName CommentField                  = "Comment"+fieldName CommentIsPrivateField         = "Comment is private"+fieldName CommentTagsField              = "Comment tags"+fieldName CommenterField                = "Commenter"+fieldName ComponentField                = "Component"+fieldName ContentField                  = "Content"+fieldName CreationDateField             = "Creation date"+fieldName DaysElapsedField              = "Days elapsed"+fieldName DependsOnField                = "Depends on"+fieldName EverConfirmedField            = "Ever confirmed"+fieldName FlagRequesteeField            = "Flag requestee"+fieldName FlagSetterField               = "Flag setter"+fieldName FlagsField                    = "Flags"+fieldName GroupField                    = "Group"+fieldName KeywordsField                 = "Keywords"+fieldName ChangedField                  = "Changed"+fieldName CommentCountField             = "Comment count"+fieldName OperatingSystemField          = "Operating system"+fieldName HardwareField                 = "Hardware"+fieldName PriorityField                 = "Priority"+fieldName ProductField                  = "Product"+fieldName QaContactField                = "QA contact"+fieldName ReporterField                 = "Reporter"+fieldName ReporterAccessibleField       = "Reporter accessible"+fieldName ResolutionField               = "Resolution"+fieldName RestrictCommentsField         = "Restrict comments"+fieldName SeeAlsoField                  = "See also"+fieldName SeverityField                 = "Severity"+fieldName StatusField                   = "Status"+fieldName WhiteboardField               = "Whiteboard"+fieldName SummaryField                  = "Summary"+fieldName TagsField                     = "Tags"+fieldName TargetMilestoneField          = "Target milestone"+fieldName TimeSinceAssigneeTouchedField = "Time since assignee touched"+fieldName BugURLField                   = "Bug URL"+fieldName VersionField                  = "Version"+fieldName VotesField                    = "Votes"+fieldName (CustomField name)            = T.concat ["Custom field \"", T.pack (show name), "\""]++searchFieldName :: Field a -> T.Text+searchFieldName AliasField                    = "alias"+searchFieldName AssignedToField               = "assigned_to"+searchFieldName AttachmentCreatorField        = "attachments.submitter"+searchFieldName AttachmentDataField           = "attach_data.thedata"+searchFieldName AttachmentDescriptionField    = "attachments.description"+searchFieldName AttachmentFilenameField       = "attachments.filename"+searchFieldName AttachmentIsObsoleteField     = "attachments.isobsolete"+searchFieldName AttachmentIsPatchField        = "attachments.ispatch"+searchFieldName AttachmentIsPrivateField      = "attachments.isprivate"+searchFieldName AttachmentMimetypeField       = "attachments.mimetype"+searchFieldName BlocksField                   = "blocked"+searchFieldName BugIdField                    = "bug_id"+searchFieldName CcField                       = "cc"+searchFieldName CcListAccessibleField         = "cclist_accessible"+searchFieldName ClassificationField           = "classification"+searchFieldName CommentField                  = "longdesc"+searchFieldName CommentIsPrivateField         = "longdescs.isprivate"+searchFieldName CommentTagsField              = "comment_tag"+searchFieldName CommenterField                = "commenter"+searchFieldName ComponentField                = "component"+searchFieldName ContentField                  = "content"+searchFieldName CreationDateField             = "creation_ts"+searchFieldName DaysElapsedField              = "days_elapsed"+searchFieldName DependsOnField                = "dependson"+searchFieldName EverConfirmedField            = "everconfirmed"+searchFieldName FlagRequesteeField            = "requestees.login_name"+searchFieldName FlagSetterField               = "setters.login_name"+searchFieldName FlagsField                    = "flagtypes.name"+searchFieldName GroupField                    = "bug_group"+searchFieldName KeywordsField                 = "keywords"+searchFieldName ChangedField                  = "delta_ts"+searchFieldName CommentCountField             = "longdescs.count"+searchFieldName OperatingSystemField          = "op_sys"+searchFieldName HardwareField                 = "rep_platform"+searchFieldName PriorityField                 = "priority"+searchFieldName ProductField                  = "product"+searchFieldName QaContactField                = "qa_contact"+searchFieldName ReporterField                 = "reporter"+searchFieldName ReporterAccessibleField       = "reporter_accessible"+searchFieldName ResolutionField               = "resolution"+searchFieldName RestrictCommentsField         = "restrict_comments"+searchFieldName SeeAlsoField                  = "see_also"+searchFieldName SeverityField                 = "bug_severity"+searchFieldName StatusField                   = "bug_status"+searchFieldName WhiteboardField               = "status_whiteboard"+searchFieldName SummaryField                  = "short_desc"+searchFieldName TagsField                     = "tag"+searchFieldName TargetMilestoneField          = "target_milestone"+searchFieldName TimeSinceAssigneeTouchedField = "owner_idle_time"+searchFieldName BugURLField                   = "bug_file_loc"+searchFieldName VersionField                  = "version"+searchFieldName VotesField                    = "votes"+searchFieldName (CustomField name)            = name++-- | A Bugzilla user.+data User = User+  { userId       :: !UserId+  , userEmail    :: Maybe UserEmail+  , userName     :: T.Text+  , userRealName :: T.Text+  } deriving (Eq, Ord, Show)++instance FromJSON User where+  parseJSON (Object v) =+    User <$> v .: "id"+         <*> v .:? "email"+         <*> v .: "name"+         <*> v .: "real_name"+  parseJSON _ = mzero++newtype UserList = UserList [User]+                deriving (Eq, Show)++instance FromJSON UserList where+  parseJSON (Object v) = UserList <$> v .: "users"+  parseJSON _          = mzero++-- | Flags, which may be set on an attachment or on a bug directly.+data Flag = Flag+  { flagId               :: !FlagId+  , flagTypeId           :: !FlagType+  , flagName             :: T.Text+  , flagSetter           :: UserEmail+  , flagStatus           :: T.Text+  , flagCreationDate     :: UTCTime+  , flagModificationDate :: UTCTime+  , flagRequestee        :: Maybe UserEmail+  } deriving (Eq, Ord, Show)++instance FromJSON Flag where+  parseJSON (Object v) =+    Flag <$> v .: "id"+         <*> v .: "type_id"+         <*> v .: "name"+         <*> v .: "setter"+         <*> v .: "status"+         <*> v .: "creation_date"+         <*> v .: "modification_date"+         <*> v .:? "requestee"+  parseJSON _ = mzero++-- | An external bug type+data ExternalType = ExternalType+  { externalTypeDescription :: T.Text+  , externalTypeUrl         :: T.Text+  , externalTypeId          :: Int+  , externalTypeType        :: T.Text+  , externalTypeFullUrl     :: T.Text+  } deriving (Eq, Ord, Show)++instance FromJSON ExternalType where+  parseJSON (Object v) =+    ExternalType <$> v .: "description"+                 <*> v .: "url"+                 <*> v .: "id"+                 <*> v .: "type"+                 <*> v .: "full_url"+  parseJSON _ = mzero++-- | An external bug.+data ExternalBug = ExternalBug+  { externalDescription    :: T.Text+  , externalBzId           :: Int+  , externalPriority       :: T.Text+  , externalBugId          :: T.Text+  , externalStatus         :: T.Text+  , externalId             :: Int+  , externalType           :: ExternalType+  } deriving (Eq, Ord, Show)++instance FromJSON ExternalBug where+  parseJSON (Object v) =+    ExternalBug <$> v .: "ext_description"+                <*> v .: "ext_bz_id"+                <*> v .: "ext_priority"+                <*> v .: "ext_bz_bug_id"+                <*> v .: "ext_status"+                <*> v .: "id"+                <*> v .: "type"+  parseJSON _ = mzero++-- | A Bugzilla bug.+data Bug = Bug+  { bugId                  :: !BugId+  , bugAlias               :: Maybe [T.Text]+  , bugAssignedTo          :: UserEmail+  , bugAssignedToDetail    :: User+  , bugBlocks              :: [BugId]+  , bugCc                  :: [UserEmail]+  , bugCcDetail            :: [User]+  , bugClassification      :: T.Text+  , bugComponent           :: [T.Text]+  , bugCreationTime        :: UTCTime+  , bugCreator             :: UserEmail+  , bugCreatorDetail       :: User+  , bugDependsOn           :: [BugId]+  , bugDupeOf              :: Maybe BugId+  , bugFlags               :: Maybe [Flag]+  , bugGroups              :: [T.Text]+  , bugIsCcAccessible      :: Bool+  , bugIsConfirmed         :: Bool+  , bugIsCreatorAccessible :: Bool+  , bugIsOpen              :: Bool+  , bugKeywords            :: [T.Text]+  , bugLastChangeTime      :: UTCTime+  , bugOpSys               :: T.Text+  , bugPlatform            :: T.Text+  , bugPriority            :: T.Text+  , bugProduct             :: T.Text+  , bugQaContact           :: UserEmail+  , bugResolution          :: T.Text+  , bugSeeAlso             :: [T.Text]+  , bugSeverity            :: T.Text+  , bugStatus              :: T.Text+  , bugSummary             :: T.Text+  , bugTargetMilestone     :: T.Text+  , bugUrl                 :: T.Text+  , bugVersion             :: [T.Text]+  , bugWhiteboard          :: T.Text+  , bugCustomFields        ::+#if MIN_VERSION_aeson(2,0,0)+                              M.KeyMap T.Text+#else+                              M.HashMap T.Text T.Text+#endif+, bugExternalBugs        :: Maybe [ExternalBug]+  } deriving (Eq, Show)++instance FromJSON Bug where+  parseJSON (Object v) =+      Bug <$> v .: "id"+          <*> v .:? "alias"+          <*> v .: "assigned_to"+          <*> v .: "assigned_to_detail"+          <*> v .: "blocks"+          <*> v .: "cc"+          <*> v .: "cc_detail"+          <*> v .: "classification"+          <*> v .: "component"+          <*> v .: "creation_time"+          <*> v .: "creator"+          <*> v .: "creator_detail"+          <*> v .: "depends_on"+          <*> v .:? "dupe_of"+          <*> v .:? "flags"+          <*> v .: "groups"+          <*> v .: "is_cc_accessible"+          <*> v .: "is_confirmed"+          <*> v .: "is_creator_accessible"+          <*> v .: "is_open"+          <*> v .: "keywords"+          <*> v .: "last_change_time"+          <*> v .: "op_sys"+          <*> v .: "platform"+          <*> v .: "priority"+          <*> v .: "product"+          <*> v .: "qa_contact"+          <*> v .: "resolution"+          <*> v .: "see_also"+          <*> v .: "severity"+          <*> v .: "status"+          <*> v .: "summary"+          <*> v .: "target_milestone"+          <*> v .: "url"+          <*> v .: "version"+          <*> v .: "whiteboard"+          <*> pure (customFields v)+          <*> v .:? "external_bugs"+  parseJSON _ = mzero++customFields :: Object ->+#if MIN_VERSION_aeson(2,0,0)+                M.KeyMap T.Text+#else+                M.HashMap T.Text T.Text+#endif+customFields = M.map stringifyCustomFields+             . M.filterWithKey filterCustomFields+  where+    stringifyCustomFields :: Value -> T.Text+    stringifyCustomFields (String t) = t+    stringifyCustomFields v          = T.concat+                                     . TL.toChunks+                                     . TLB.toLazyText+                                     . encodeToTextBuilder+                                     . toJSON+                                     $ v++    filterCustomFields k _ = "cf_" `T.isPrefixOf` toText k+#if !MIN_VERSION_aeson(2,0,0)+      where toText = id+#endif++newtype BugList = BugList [Bug]+               deriving (Eq, Show)++instance FromJSON BugList where+  parseJSON (Object v) = BugList <$> v .: "bugs"+  parseJSON _          = mzero++newtype BugIdList = BugIdList [BugId]+                 deriving (Eq, Show)++instance FromJSON BugIdList where+  parseJSON (Object v) = do+    bugs <- v .: "bugs"+    bugIds <- mapM (.: "id") bugs+    return $ BugIdList bugIds+  parseJSON _          = mzero++-- | An attachment to a bug.+data Attachment = Attachment+  { attachmentId             :: !AttachmentId+  , attachmentBugId          :: !BugId+  , attachmentFileName       :: T.Text+  , attachmentSummary        :: T.Text+  , attachmentCreator        :: UserEmail+  , attachmentIsPrivate      :: Bool+  , attachmentIsObsolete     :: Bool+  , attachmentIsPatch        :: Bool+  , attachmentFlags          :: [Flag]+  , attachmentCreationTime   :: UTCTime+  , attachmentLastChangeTime :: UTCTime+  , attachmentContentType    :: T.Text+  , attachmentSize           :: !Int+  , attachmentData           :: T.Text+  } deriving (Eq, Show)++instance FromJSON Attachment where+  parseJSON (Object v) =+    Attachment <$> v .: "id"+               <*> v .: "bug_id"+               <*> v .: "file_name"+               <*> v .: "summary"+               <*> v .: "creator"+               <*> (fromNumericBool <$> v .: "is_private")+               <*> (fromNumericBool <$> v .: "is_obsolete")+               <*> (fromNumericBool <$> v .: "is_patch")+               <*> v .: "flags"+               <*> v .: "creation_time"+               <*> v .: "last_change_time"+               <*> v .: "content_type"+               <*> v .: "size"+               <*> v .: "data"+  parseJSON _ = mzero++fromNumericBool :: Int -> Bool+fromNumericBool 0 = False+fromNumericBool _ = True++newtype AttachmentList = AttachmentList [Attachment]+                      deriving (Eq, Show)++instance FromJSON AttachmentList where+  parseJSON (Object v) = do+    attachmentsVal <- v .: "attachments"+    bugsVal <- v .: "bugs"+    case (attachmentsVal, bugsVal) of+      (Object (M.toList -> [(_, as)]), _) -> AttachmentList . (:[]) <$> parseJSON as+      (_, Object (M.toList -> [(_, as)])) -> AttachmentList <$> parseJSON as+      _                                   -> mzero+  parseJSON _ = mzero++-- | A bug comment. To display these the way Bugzilla does, you'll+-- need to call 'getUser' and use the 'userRealName' for each user.+data Comment = Comment+  { commentId           :: !CommentId+  , commentBugId        :: !BugId+  , commentAttachmentId :: Maybe AttachmentId+  , commentCount        :: !Int+  , commentText         :: T.Text+  , commentCreator      :: UserEmail+  , commentCreationTime :: UTCTime+  , commentIsPrivate    :: Bool+  } deriving (Eq, Show)++instance FromJSON Comment where+  parseJSON (Object v) =+    Comment <$> v .: "id"+            <*> v .: "bug_id"+            <*> v .: "attachment_id"+            <*> v .: "count"+            <*> v .: "text"+            <*> v .: "creator"+            <*> v .: "creation_time"+            <*> v .: "is_private"+  parseJSON _ = mzero++newtype CommentList = CommentList [Comment]+                   deriving (Eq, Show)++instance FromJSON CommentList where+  parseJSON (Object v) = do+    bugsVal <- v .: "bugs"+    case bugsVal of+      Object (M.toList -> [(_, cs)]) ->+        do comments <- withObject "comments" (.: "comments") cs+           withArray "comment list" (\a -> CommentList <$> parseJSON (addCount a)) comments+      _ -> mzero+  parseJSON _ = mzero++-- Note that we make the (possibly unwise) assumption that Bugzilla+-- returns the comments in order. If it turns out that's not true, we+-- can always sort by their 'id' to ensure correct results.+addCount :: V.Vector Value -> Value+addCount vs = Array $ V.zipWith addCount' (V.enumFromN 0 $ V.length vs) vs+ where+   addCount' :: Int -> Value -> Value+   addCount' c (Object v) = Object $ M.insert "count" (Number $ fromIntegral c) v+   addCount' _ v          = v++-- | History information for a bug.+data History = History+  { historyBugId   :: !BugId+  , historyEvents  :: [HistoryEvent]+  } deriving (Eq, Show)++instance FromJSON History where+  parseJSON (Object v) = do+    bugsVal <- v .: "bugs"+    case bugsVal of+      Array (V.toList -> [history]) ->+        withObject "history"+                   (\h -> History <$> h .: "id"+                                  <*> parseHistoryEvents h)+                   history+      _ -> mzero+  parseJSON _ = mzero++parseHistoryEvents :: Object -> Parser [HistoryEvent]+parseHistoryEvents h = do+  events <- h .: "history"+  withArray "event list" (parseJSON . addCount) events++-- | An event in a bug's history.+data HistoryEvent = HistoryEvent+  { historyEventId      :: EventId   -- ^ A sequential event id.+  , historyEventTime    :: UTCTime   -- ^ When the event occurred.+  , historyEventUser    :: UserEmail -- ^ Which user was responsible.+  , historyEventChanges :: [Change]  -- ^ All the changes which are+                                     --   part of this event.+  } deriving (Eq, Show)++instance FromJSON HistoryEvent where+  parseJSON (Object v) =+    HistoryEvent <$> v .: "count"+                 <*> v .: "when"+                 <*> v .: "who"+                 <*> v .: "changes"+  parseJSON _ = mzero++-- | A single change which is part of an event. Different constructors+--   are used according to the type of the field. The 'Modification'+--   describes the value of the field before and after the change.+data Change+  = TextFieldChange (Field T.Text) (Modification T.Text)+  | ListFieldChange (Field [T.Text]) (Modification [T.Text])+  | IntFieldChange (Field Int) (Modification Int)+  | TimeFieldChange (Field UTCTime) (Modification UTCTime)+  | BoolFieldChange (Field Bool) (Modification Bool)+    deriving (Eq, Show)++instance FromJSON Change where+  parseJSON (Object v) = do+    changedField <- v .: "field_name"+    case changedField of+      "alias"                  -> ListFieldChange AliasField <$> parseModification v+      "assigned_to"            -> TextFieldChange AssignedToField <$> parseModification v+      "attachments.submitter"  -> TextFieldChange AttachmentCreatorField <$> parseModification v+      "attach_data.thedata"    -> TextFieldChange AttachmentDataField <$> parseModification v+      "attachments.description"-> TextFieldChange AttachmentDescriptionField <$> parseModification v+      "attachments.filename"   -> TextFieldChange AttachmentFilenameField <$> parseModification v+      "attachments.isobsolete" -> BoolFieldChange AttachmentIsObsoleteField <$> parseModification v+      "attachments.ispatch"    -> BoolFieldChange AttachmentIsPatchField <$> parseModification v+      "attachments.isprivate"  -> BoolFieldChange AttachmentIsPrivateField <$> parseModification v+      "attachments.mimetype"   -> TextFieldChange AttachmentMimetypeField <$> parseModification v+      "blocks"                 -> IntFieldChange BlocksField <$> parseModification v+      "bug_id"                 -> IntFieldChange BugIdField <$> parseModification v+      "cc"                     -> TextFieldChange CcField <$> parseModification v+      "is_cc_accessible"       -> BoolFieldChange CcListAccessibleField <$> parseModification v+      "classification"         -> TextFieldChange ClassificationField <$> parseModification v+      "component"              -> ListFieldChange ComponentField <$> parseModification v+      "content"                -> TextFieldChange ContentField <$> parseModification v+      "creation_time"          -> TimeFieldChange CreationDateField <$> parseModification v+      "days_elapsed"           -> IntFieldChange DaysElapsedField <$> parseModification v+      "depends_on"             -> IntFieldChange DependsOnField <$> parseModification v+      "everconfirmed"          -> BoolFieldChange EverConfirmedField <$> parseModification v+      "flagtypes.name"         -> TextFieldChange FlagsField <$> parseModification v+      "bug_group"              -> TextFieldChange GroupField <$> parseModification v+      "keywords"               -> ListFieldChange KeywordsField <$> parseModification v+      "op_sys"                 -> TextFieldChange OperatingSystemField <$> parseModification v+      "platform"               -> TextFieldChange HardwareField <$> parseModification v+      "priority"               -> TextFieldChange PriorityField <$> parseModification v+      "product"                -> TextFieldChange ProductField <$> parseModification v+      "qa_contact"             -> TextFieldChange QaContactField <$> parseModification v+      "reporter"               -> TextFieldChange ReporterField <$> parseModification v+      "reporter_accessible"    -> BoolFieldChange ReporterAccessibleField <$> parseModification v+      "resolution"             -> TextFieldChange ResolutionField <$> parseModification v+      "restrict_comments"      -> BoolFieldChange RestrictCommentsField <$> parseModification v+      "see_also"               -> TextFieldChange SeeAlsoField <$> parseModification v+      "severity"               -> TextFieldChange SeverityField <$> parseModification v+      "status"                 -> TextFieldChange StatusField <$> parseModification v+      "whiteboard"             -> TextFieldChange WhiteboardField <$> parseModification v+      "summary"                -> TextFieldChange SummaryField <$> parseModification v+      "tag"                    -> TextFieldChange TagsField <$> parseModification v+      "target_milestone"       -> TextFieldChange TargetMilestoneField <$> parseModification v+      "url"                    -> TextFieldChange BugURLField <$> parseModification v+      "version"                -> TextFieldChange VersionField <$> parseModification v+      "votes"                  -> TextFieldChange VotesField <$> parseModification v+      name                     -> TextFieldChange (CustomField name) <$> parseModification v+  parseJSON _ = mzero++-- | A description of how a field changed during a 'HistoryEvent'.+data (Eq a, Show a) => Modification a = Modification+  { modRemoved      :: Maybe a+  , modAdded        :: Maybe a+  , modAttachmentId :: Maybe AttachmentId+  } deriving (Eq, Show)++parseModification :: (FromJSON a, Eq b, Show b, ToModification a b) => Object -> Parser (Modification b)+parseModification v = Modification <$> (toMod =<< v .: "removed")+                                   <*> (toMod =<< v .: "added")+                                   <*> v .:? "attachment_id"++class ToModification a b | b -> a where toMod :: a -> Parser (Maybe b)+instance ToModification T.Text T.Text where toMod = return . Just+instance ToModification UTCTime UTCTime where toMod = return . Just++instance ToModification T.Text Int where+  toMod v | v == "" = return Nothing+          | otherwise = case TR.decimal v of+                          Left _       -> mzero+                          Right (i, _) -> return $ Just i++instance ToModification T.Text Bool where+  toMod v | v == "0"  = return $ Just False+          | v == "1"  = return $ Just True+          | otherwise = mzero++instance ToModification T.Text [T.Text] where+  toMod v = return . Just $ T.splitOn ", " v
+ src/Web/RedHatBugzilla/Search.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE OverloadedStrings #-}++-- | A modified version of Web.Bugzilla.Search to support+--   the list fields in Red Hat's modified bugzilla API.++module Web.RedHatBugzilla.Search+(+  -- * Search operators+  (.==.)+, (./=.)+, (.<.)+, (.<=.)+, (.>.)+, (.>=.)+, (.=~.)+, (./=~.)+, equalsAny+, contains+, containsCase+, containsAny+, containsAll+, changedBefore+, changedAfter+, changedSince+, changedUntil+, changedRange+, changedFrom+, changedTo+, changedBy+, contentMatches+, isEmpty+, isNotEmpty+, (.&&.)+, (.||.)+, not'++  -- * Search expressions+, Field (..)+, SearchExpression+, evalSearchExpr+) where++import qualified Data.Text as T+import Data.Time.Clock (UTCTime(..))++import Web.RedHatBugzilla.Internal.Search+import Web.RedHatBugzilla.Internal.Types++(.==.) :: FieldType a => Field a -> a -> SearchExpression+(.==.) = (Term .) . BinaryOp "equals"+infix 4 .==.++(./=.) :: FieldType a => Field a -> a -> SearchExpression+(./=.) = (Term .) . BinaryOp "notequals"+infix 4 ./=.++(.<.) :: FieldType a => Field a -> a -> SearchExpression+(.<.) = (Term .) . BinaryOp "lessthan"+infix 4 .<.++(.<=.) :: FieldType a => Field a -> a -> SearchExpression+(.<=.) = (Term .) . BinaryOp "lessthaneq"+infix 4 .<=.++(.>.) :: FieldType a => Field a -> a -> SearchExpression+(.>.) = (Term .) . BinaryOp "greaterthan"+infix 4 .>.++(.>=.) :: FieldType a => Field a -> a -> SearchExpression+(.>=.) = (Term .) . BinaryOp "greaterthaneq"+infix 4 .>=.++(.=~.) :: FieldType a => Field a -> a -> SearchExpression+(.=~.) = (Term .) . BinaryOp "regexp"++(./=~.) :: FieldType a => Field a -> a -> SearchExpression+(./=~.) = (Term .) . BinaryOp "notregexp"++equalsAny :: FieldType a => Field a -> [a] -> SearchExpression+equalsAny = (Term .) . BinaryOp "anyexact"++contains :: Field T.Text -> T.Text -> SearchExpression+contains = (Term .) . BinaryOp "substring"++containsCase :: Field T.Text -> T.Text -> SearchExpression+containsCase = (Term .) . BinaryOp "casesubstring"++containsAny :: Field T.Text -> [T.Text] -> SearchExpression+containsAny = (Term .) . BinaryOp "anywordssubstr"++containsAll :: Field T.Text -> [T.Text] -> SearchExpression+containsAll = (Term .) . BinaryOp "allwordssubstr"++changedBefore :: FieldType a => Field a -> UTCTime -> SearchExpression+changedBefore = (Term .) . BinaryOp "changedbefore"++changedAfter :: FieldType a => Field a -> UTCTime -> SearchExpression+changedAfter = (Term .) . BinaryOp "changedafter"++-- | Filter bug changed since UTCTime+changedSince :: UTCTime -> SearchExpression+changedSince ts = Term $ EqTerm (CustomField "chfieldfrom") ts++-- | Filter bug changed until UTCTime+changedUntil :: UTCTime -> SearchExpression+changedUntil ts = Term $ EqTerm (CustomField "chfieldto") ts++-- | Filter bug changed in range+changedRange :: UTCTime -> UTCTime -> SearchExpression+changedRange from to = changedSince from .&&. changedUntil to++changedFrom :: FieldType a => Field a -> a -> SearchExpression+changedFrom = (Term .) . BinaryOp "changedfrom"++changedTo :: FieldType a => Field a -> a -> SearchExpression+changedTo = (Term .) . BinaryOp "changedto"++changedBy :: FieldType a => Field a -> UserEmail -> SearchExpression+changedBy = (Term .) . BinaryOp "changedby"++contentMatches :: T.Text -> SearchExpression+contentMatches = Term . BinaryOp "matches" ContentField++isEmpty :: FieldType a => Field a -> SearchExpression+isEmpty = Term . UnaryOp "isempty"++isNotEmpty :: FieldType a => Field a -> SearchExpression+isNotEmpty = Term . UnaryOp "isnotempty"++(.&&.) :: SearchExpression -> SearchExpression -> SearchExpression+(.&&.) (And as) (And bs) = And (as ++ bs)+(.&&.) (And as) a = And (as ++ [a])+(.&&.) a (And as) = And (a:as)+(.&&.) a b        = And [a, b]+infixr 3 .&&.++(.||.) :: SearchExpression -> SearchExpression -> SearchExpression+(.||.) (Or as) (Or bs) = Or (as ++ bs)+(.||.) a (Or as) = Or (a:as)+(.||.) (Or as) a = Or (as ++ [a])+(.||.) a b       = Or [a, b]+infixr 2 .||.++not' :: SearchExpression -> SearchExpression+not' (Not a) = a+not' a       = Not a
test/Spec.hs view
@@ -4,7 +4,7 @@ module Main (main) where  import Test.Hspec-import Web.Bugzilla.RedHat.Search+import Web.RedHatBugzilla.Search  main :: IO () main = hspec spec