{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE ScopedTypeVariables #-} module Zendesk ( runZendesk , createUser , getUsers , getTickets , getTicketFields , ZendeskConfig(..) , runZendeskT ) where import Control.Applicative ((<$>), (<*>)) import Control.Exception (catches, SomeException(..), Handler(..)) import Control.Failure (Failure(..)) import Control.Monad (liftM, forM) import Control.Monad.Reader (ReaderT (..), asks) import Control.Monad.Error (ErrorT (..), MonadError(..), Error(..)) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Logger (MonadLogger(..), logDebug) import Control.Monad.Trans (lift) import Data.Aeson as J (eitherDecode, encode, FromJSON(..), withObject, withText, (.:), object, (.=), Value(..), Object) import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as LBS (toStrict) import Data.ByteString.Char8 as BS8 (pack, unpack) import Data.CaseInsensitive (mk) import Data.Default (Default(..)) import qualified Data.HashMap.Strict as M import Data.Int (Int64) import Data.Maybe (fromJust) import Data.PEM (PEM(..), pemParseBS) import Data.Text as T (pack, Text, unpack) import Data.Text.Encoding as T (encodeUtf8) import Data.Time.Clock (UTCTime(..)) import Data.Traversable (Traversable(..)) import Data.X509 (HashALG(..), decodeSignedCertificate) import Data.X509.CertificateStore (CertificateStore, makeCertificateStore) import Data.X509.Validation (ValidationChecks(..), validate, defaultHooks, defaultChecks) import Network.Connection (TLSSettings(..)) import Network.HTTP.Client (getUri, applyBasicAuth) import Network.HTTP.Client.TLS (mkManagerSettings) import Network.HTTP.Conduit as HTTP ( httpLbs, parseUrl, withManagerSettings , HttpException(..) , Request(..), Response(..) , responseBody, RequestBody(..), requestBody , method, requestHeaders, checkStatus) import Network.HTTP.Types.Status (Status(..)) import Network.TLS (Credential, Credentials(..), ClientParams(..), Shared(..), ClientHooks(..), Supported(..), defaultParamsClient, credentialLoadX509FromMemory) import Network.TLS.Extra.Cipher (ciphersuite_strong) import Json (deriveJSON, deriveJSON_, deriveEnumJSON) import Data.Conduit (Source, yield) import Zendesk.Common import Zendesk.User getTicketsUrl :: Monad m => ZendeskT m String getTicketsUrl = do baseUrl <- asks zendeskUrl return $ baseUrl ++ "/api/v2/tickets.json" getTicketFieldsUrl :: Monad m => ZendeskT m String getTicketFieldsUrl = do baseUrl <- asks zendeskUrl return $ baseUrl ++ "/api/v2/ticket_fields.json" data TicketField = TicketField { ticketFieldId :: Maybe Int , ticketFieldUrl :: Maybe Text , ticketFieldType :: Text , ticketFieldTitle :: Text , ticketFieldDescription :: Maybe Text , ticketFieldPosition :: Maybe Int , ticketFieldActive :: Maybe Bool , ticketFieldRequired :: Maybe Bool , ticketFieldCollapsedForAgents :: Maybe Bool , ticketFieldRegexpForValidation :: Maybe Text , ticketFieldTitleInPortal :: Maybe Text , ticketFieldVisibleInPortal :: Maybe Bool , ticketFieldEditableInPortal :: Maybe Bool , ticketFieldRequiredInPortal :: Maybe Bool , ticketFieldTag :: Maybe Text , ticketFieldCreatedAt :: Maybe UTCTime , ticketFieldUpdatedAt :: Maybe UTCTime --, ticketSystemFieldOptions :: Maybe [Text] TODO Array of something --, ticketCustomFieldOptions :: Maybe [Text] TODO Array of something , ticketFieldRemovable :: Maybe Bool } deriving (Show) data TicketFieldValue = TicketFieldValue { ticketFieldValueId :: Int , ticketFieldValueValue :: Maybe Text } deriving (Show) data Ticket = Ticket { ticketId :: Maybe Int , ticketUrl :: Maybe Text , ticketExternalId :: Maybe Text , ticketType :: Maybe Text , ticketSubject :: Maybe Text , ticketDescription :: Maybe Text , ticketPriority :: Maybe Text , ticketStatus :: Maybe Text , ticketRecipient :: Maybe Text , ticketRequesterId :: Int , ticketSubmitterId :: Maybe Int , ticketAssigneeId :: Maybe Int , ticketOrganizationId :: Maybe Int , ticketGroupId :: Maybe Int , ticketCollaboratorIds :: Maybe [Int] , ticketForumTopicId :: Maybe Int , ticketProblemId :: Maybe Int , ticketHasIncidents :: Maybe Bool , ticketDueAt :: Maybe Text , ticketTags :: Maybe [Text] , ticketVia :: Maybe Via , ticketCustomFields :: Maybe [TicketFieldValue] , ticketSatisfactionRaiting :: Maybe Object , ticketSharingAgreementIds :: Maybe [Int] , ticketFollowupIds :: Maybe [Int] , ticketTicketFormId :: Maybe Int , ticketBrandId :: Maybe Int , ticketCreatedAt :: Maybe UTCTime , ticketUpdatedAt :: Maybe UTCTime } deriving (Show) data Via = Via { viaChannel :: Text , viaSource :: Maybe Object } deriving (Show) instance CollectionKey Ticket where collectionKey _ = "tickets" instance CollectionKey TicketField where collectionKey _ = "ticket_fields" deriveJSON ''Ticket deriveJSON ''TicketField deriveJSON ''TicketFieldValue deriveJSON ''Via getTickets :: (MonadIO m, MonadLogger m) => Source (ZendeskT m) Ticket getTickets = getCollection =<< (Just `liftM` (T.pack `liftM` (lift getTicketsUrl))) getTicketFields :: (MonadIO m, MonadLogger m) => Source (ZendeskT m) TicketField getTicketFields = getCollection =<< (Just `liftM` (T.pack `liftM` (lift getTicketFieldsUrl)))