git.iko.soy/typeable/pagerduty
on develop help sign in
Merge branch 'feature/pagination' into develop
commita893bcde92fde6fb9e6815451a43788fa5c54627 authorBrendan Hay <brendan.g.hay@gmail.com> dateFri 24 Oct 2014, 11:22 (12 years ago) parent4e33828d1d0c71
37 files changed+6357−1011
Makefile+3 pagerduty.cabal+61−32 script/generate-pages+25 script/upload-documentation+37 src/Control/Monad/Trans/PagerDuty.hs+207 src/Network/PagerDuty.hs−50 src/Network/PagerDuty/API.hs−30 src/Network/PagerDuty/API/Integration.hs−80 src/Network/PagerDuty/API/LogEntries.hs−30 src/Network/PagerDuty/API/MaintenanceWindows.hs−238 src/Network/PagerDuty/API/Services.hs−302 src/Network/PagerDuty/IO.hs−78 src/Network/PagerDuty/Integration.hs+292 src/Network/PagerDuty/Internal.hs−45 src/Network/PagerDuty/Internal/IO.hs+76 src/Network/PagerDuty/Internal/Options.hs+56 src/Network/PagerDuty/Internal/Query.hs+109 src/Network/PagerDuty/Internal/TH.hs+96 src/Network/PagerDuty/Internal/Types.hs+480 src/Network/PagerDuty/REST.hs+104 src/Network/PagerDuty/REST/Alerts.hs+123 src/Network/PagerDuty/REST/EscalationPolicies.hs+244 src/Network/PagerDuty/REST/EscalationPolicies/EscalationRules.hs+277 src/Network/PagerDuty/REST/Incidents.hs+650 src/Network/PagerDuty/REST/Incidents/Notes.hs+108 src/Network/PagerDuty/REST/LogEntries.hs+514 src/Network/PagerDuty/REST/MaintenanceWindows.hs+289 src/Network/PagerDuty/REST/Reports.hs+188 src/Network/PagerDuty/REST/Schedules.hs+638 src/Network/PagerDuty/REST/Schedules/Overrides.hs+188 src/Network/PagerDuty/REST/Services.hs+513 src/Network/PagerDuty/REST/Services/EmailFilters.hs+180 src/Network/PagerDuty/REST/Users.hs+384 src/Network/PagerDuty/REST/Users/ContactMethods.hs+236 src/Network/PagerDuty/REST/Users/NotificationRules.hs+164 src/Network/PagerDuty/REST/Webhooks.hs+115 src/Network/PagerDuty/Types.hs−126
Makefile+3
@@ -3,6 +3,9 @@ FLAGS := -j --disable-documentation --disable-library-coverage
33build:
44 cabal build $(addprefix -,$(findstring j,$(MAKEFLAGS)))
55
6+all:
7+ make clean; make install && make build
8+
69install: cabal.sandbox.config
710 cabal install $(FLAGS) --only-dependencies
811
pagerduty.cabal+61−32
@@ -1,21 +1,24 @@
11name: pagerduty
2−version: 0.1.0
2+version: 0.0.0
33synopsis: Pager Duty API Client
44homepage: http://github.com/brendanhay/pagerduty
55license: OtherLicense
66license-file: LICENSE
7−author: Brendan Hay, Kim Altintop
8−maintainer: Brendan Hay <brendan.g.hay@gmail.com>,
9− Kim Altintop <kim.altintop@gmail.com>
7+author: Brendan Hay
8+maintainer: Brendan Hay <brendan.g.hay@gmail.com>
109copyright: Copyright (c) 2013-2014 Brendan Hay, 2014 Kim Altintop
11−category: Network, Monitoring
10+category: Network, Monitoring, Alerting
1211build-type: Simple
1312cabal-version: >= 1.10
1413
1514description:
16− Simple API client that wraps the PagerDuty REST APIs documented here:
15+ Client library for the the PagerDuty REST APIs documented here:
1716 .
18− * Integration API - <http://developer.pagerduty.com/documentation/integration/events>
17+ * Integration API - <http://developer.pagerduty.com/documentation/integration>
18+ * REST API - <http://developer.pagerduty.com/documentation/rest>
19+ .
20+ /Warning:/ This is an experimental preview release which is still under
21+ heavy development and not intended for public consumption, caveat emptor!
1922
2023extra-source-files:
2124 README.md
@@ -28,31 +31,57 @@ library
2831 default-language: Haskell2010
2932 hs-source-dirs: src
3033
34+ ghc-options: -Wall
35+
3136 exposed-modules:
32− Network.PagerDuty
33− , Network.PagerDuty.API
34− , Network.PagerDuty.API.Integration
35− , Network.PagerDuty.API.MaintenanceWindows
36− , Network.PagerDuty.API.Services
37− , Network.PagerDuty.IO
38− , Network.PagerDuty.Internal
39− , Network.PagerDuty.Types
40−
41− ghc-options:
42− -Wall
43− -fwarn-tabs
44− -funbox-strict-fields
37+ Control.Monad.Trans.PagerDuty
38+ , Network.PagerDuty.Integration
39+ , Network.PagerDuty.REST
40+ , Network.PagerDuty.REST.Alerts
41+ , Network.PagerDuty.REST.EscalationPolicies
42+ , Network.PagerDuty.REST.EscalationPolicies.EscalationRules
43+ , Network.PagerDuty.REST.Incidents
44+ , Network.PagerDuty.REST.Incidents.Notes
45+ , Network.PagerDuty.REST.LogEntries
46+ , Network.PagerDuty.REST.MaintenanceWindows
47+ , Network.PagerDuty.REST.Reports
48+ , Network.PagerDuty.REST.Schedules
49+ , Network.PagerDuty.REST.Schedules.Overrides
50+ , Network.PagerDuty.REST.Services
51+ , Network.PagerDuty.REST.Services.EmailFilters
52+ , Network.PagerDuty.REST.Users
53+ , Network.PagerDuty.REST.Users.ContactMethods
54+ , Network.PagerDuty.REST.Users.NotificationRules
55+ , Network.PagerDuty.REST.Webhooks
56+
57+ other-modules:
58+ Network.PagerDuty.Internal.IO
59+ , Network.PagerDuty.Internal.Options
60+ , Network.PagerDuty.Internal.Query
61+ , Network.PagerDuty.Internal.TH
62+ , Network.PagerDuty.Internal.Types
4563
4664 build-depends:
47− aeson ==0.7.*
48− , base ==4.*
49− , base64-bytestring
50− , bytestring
51− , data-default
52− , http-client ==0.3.*
53− , http-client-tls ==0.2.*
54− , http-types
55− , mtl
56− , text
57− , time
58− , unordered-containers
65+ aeson > 0.7 && <= 0.8
66+ , base >= 4.5 && < 5.0
67+ , bifunctors >= 4
68+ , bytestring >= 0.9
69+ , bytestring-conversion >= 0.2.1
70+ , conduit >= 0.5.5
71+ , data-default-class >= 0.0.1
72+ , exceptions >= 0.6
73+ , generics-sop >= 0.1.0.2
74+ , http-client >= 0.3.6
75+ , http-types >= 0.8
76+ , lens >= 4.0
77+ , lens-aeson >= 1.0
78+ , mmorph >= 1.0
79+ , monad-control >= 0.3.2
80+ , mtl >= 2.2.1
81+ , old-locale >= 1.0.0.5
82+ , template-haskell >= 2.2
83+ , text >= 1.1
84+ , time == 1.4.*
85+ , transformers >= 0.4.1
86+ , transformers-base >= 0.4.1
87+ , unordered-containers >= 0.2.3
script/generate-pagesnew file+25
@@ -0,0 +1,25 @@
1+#!/usr/bin/env bash
2+
3+set -e
4+set -x
5+set -o errexit
6+
7+branch=$(git symbolic-ref -q HEAD)
8+branch=${branch##refs/heads/}
9+branch=${branch:-HEAD}
10+
11+cabal configure --disable-tests
12+
13+DOCDIR=`mktemp -d -t pages.XXXXXX`
14+cabal haddock --haddock-option=-o$DOCDIR
15+
16+HEAD=`git rev-parse HEAD`
17+
18+git checkout gh-pages
19+
20+mv $DOCDIR/* .
21+
22+git add *.html *.js *.css *.gif *.png
23+git commit -m "Generated documentation from $HEAD on $branch"
24+git push origin gh-pages
25+git checkout $branch
script/upload-documentationnew file+37
@@ -0,0 +1,37 @@
1+#!/usr/bin/env bash
2+
3+set -e
4+
5+package=${PWD##*/}
6+
7+echo "Package: ${package}"
8+read -p "Version: " version
9+read -p "Hackage user: " user
10+read -sp "Hackage password (not echoed): " password
11+
12+echo
13+echo "BUILDING"
14+echo
15+
16+cabal configure
17+cabal build
18+cabal haddock \
19+ --hyperlink-source \
20+ --html-location='/package/$pkg-$version/docs' \
21+ --contents-location='/package/$pkg'
22+
23+echo
24+echo "ARCHIVING"
25+echo
26+
27+cd dist/doc/html
28+DDIR="${package}-${version}-docs"
29+cp -r "${package}" "${DDIR}" && gtar -c -v -z -Hustar -f "${DDIR}.tar.gz" "${DDIR}"
30+
31+echo
32+echo "UPLOADING"
33+echo
34+
35+curl -X PUT -H 'Content-Type: application/x-tar' \
36+ -H 'Content-Encoding: gzip' --data-binary "@${DDIR}.tar.gz" \
37+ "http://${user}:${password}@hackage.haskell.org/package/${package}-${version}/docs"
src/Control/Monad/Trans/PagerDuty.hsnew file+207
@@ -0,0 +1,207 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE TemplateHaskell #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+
10+-- Module : Control.Monad.Trans.PagerDuty
11+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
12+-- License : This Source Code Form is subject to the terms of
13+-- the Mozilla Public License, v. 2.0.
14+-- A copy of the MPL can be found in the LICENSE file or
15+-- you can obtain it at http://mozilla.org/MPL/2.0/.
16+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
17+-- Stability : experimental
18+-- Portability : non-portable (GHC extensions)
19+
20+module Control.Monad.Trans.PagerDuty
21+ (
22+ -- * Transformer
23+ PagerDuty
24+ , PagerDutyT
25+
26+ -- * Running
27+ , runPagerDutyT
28+
29+ -- * Configuration
30+ -- ** Environment
31+ , Env
32+ , envDomain
33+ , envAuth
34+ , envManager
35+ , envLogger
36+ , scoped
37+
38+ -- ** Logging
39+ , Logger (..)
40+ , debug
41+
42+ -- * Integration events
43+ , submit
44+ , submitCatch
45+
46+ -- * REST requests
47+ , send
48+ , sendCatch
49+ , paginate
50+ , paginateCatch
51+
52+ -- * Errors
53+ , Error(..)
54+ , _Internal
55+ , _Integration
56+ , _REST
57+
58+ -- ** Integration
59+ , IntegrationError
60+ , status
61+
62+ -- ** REST
63+ , RESTError
64+ , code
65+
66+ -- ** Code
67+ , Code (..)
68+ , description
69+
70+ -- ** Fields
71+ , message
72+ , errors
73+
74+ -- ** Lifting
75+ , hoistError
76+ ) where
77+
78+import Control.Applicative
79+import Control.Monad.Base
80+import Control.Monad.Catch
81+import Control.Monad.Except
82+import Control.Monad.Morph
83+import Control.Monad.Reader
84+import Control.Monad.Trans.Control
85+import Data.Aeson
86+import Data.Conduit
87+import Network.PagerDuty.Integration (Event, Response)
88+import qualified Network.PagerDuty.Integration as Int
89+import Network.PagerDuty.Internal.Types
90+import qualified Network.PagerDuty.REST as REST
91+
92+-- | A convenient alias for 'PagerDutyT' 'IO'.
93+type PagerDuty s = PagerDutyT s IO
94+
95+newtype PagerDutyT s m a = PagerDutyT
96+ { unPagerDutyT :: ReaderT (Env s) (ExceptT Error m) a
97+ } deriving
98+ ( Functor
99+ , Applicative
100+ , Monad
101+ , MonadIO
102+ , MonadThrow
103+ , MonadCatch
104+ , MonadReader (Env s)
105+ , MonadError Error
106+ )
107+
108+instance MonadTrans (PagerDutyT s) where
109+ lift = PagerDutyT . lift . lift
110+ {-# INLINE lift #-}
111+
112+instance MonadBase b m => MonadBase b (PagerDutyT s m) where
113+ liftBase = liftBaseDefault
114+ {-# INLINE liftBase #-}
115+
116+instance MonadTransControl (PagerDutyT s) where
117+ newtype StT (PagerDutyT s) a = StTPDT
118+ { unStT :: StT (ExceptT Error) (StT (ReaderT (Env s)) a)
119+ }
120+
121+ liftWith f = PagerDutyT $
122+ liftWith $ \g ->
123+ liftWith $ \h ->
124+ f (liftM StTPDT . h . g . unPagerDutyT)
125+ {-# INLINE liftWith #-}
126+
127+ restoreT = PagerDutyT . restoreT . restoreT . liftM unStT
128+ {-# INLINE restoreT #-}
129+
130+instance MonadBaseControl b m => MonadBaseControl b (PagerDutyT s m) where
131+ newtype StM (PagerDutyT s m) a = StMPDT
132+ { unStMPDT :: ComposeSt (PagerDutyT s) m a
133+ }
134+
135+ liftBaseWith = defaultLiftBaseWith StMPDT
136+ {-# INLINE liftBaseWith #-}
137+
138+ restoreM = defaultRestoreM unStMPDT
139+ {-# INLINE restoreM #-}
140+
141+instance MFunctor (PagerDutyT s) where
142+ hoist nat m = PagerDutyT (ReaderT (ExceptT . nat . runPagerDutyT m))
143+ {-# INLINE hoist #-}
144+
145+instance MMonad (PagerDutyT s) where
146+ embed f m = ask >>= f . runPagerDutyT m >>= either throwError return
147+ {-# INLINE embed #-}
148+
149+runPagerDutyT :: PagerDutyT s m a -> Env s -> m (Either Error a)
150+runPagerDutyT (PagerDutyT k) = runExceptT . runReaderT k
151+
152+hoistError :: (MonadError Error m) => Either Error a -> m a
153+hoistError = either throwError return
154+
155+scoped :: MonadReader (Env s) m => (Env s -> m a) -> m a
156+scoped f = ask >>= f
157+
158+submit :: ( MonadIO m
159+ , MonadReader (Env s) m
160+ , MonadError Error m
161+ )
162+ => Event
163+ -> m Response
164+submit = submitCatch >=> hoistError
165+
166+submitCatch :: ( MonadIO m
167+ , MonadReader (Env s) m
168+ )
169+ => Event
170+ -> m (Either Error Response)
171+submitCatch x = scoped $ \e -> Int.submitWith (_envManager e) (_envLogger e) x
172+
173+send :: ( MonadIO m
174+ , MonadReader (Env s) m
175+ , MonadError Error m
176+ , FromJSON b
177+ )
178+ => Request a s b
179+ -> m b
180+send = sendCatch >=> hoistError
181+
182+sendCatch :: ( MonadIO m
183+ , MonadReader (Env s) m
184+ , FromJSON b
185+ )
186+ => Request a s b
187+ -> m (Either Error b)
188+sendCatch x = scoped (`REST.sendWith` x)
189+
190+paginate :: ( MonadIO m
191+ , MonadReader (Env s) m
192+ , MonadError Error m
193+ , Paginate a
194+ , FromJSON b
195+ )
196+ => Request a s b
197+ -> Source m b
198+paginate x = paginateCatch x $= awaitForever (hoistError >=> yield)
199+
200+paginateCatch :: ( MonadIO m
201+ , MonadReader (Env s) m
202+ , Paginate a
203+ , FromJSON b
204+ )
205+ => Request a s b
206+ -> Source m (Either Error b)
207+paginateCatch x = scoped (`REST.paginateWith` x)
src/Network/PagerDuty.hsdeleted−50
@@ -1,50 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−-- Module : Network.PagerDuty
4−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
5−-- License : This Source Code Form is subject to the terms of
6−-- the Mozilla Public License, v. 2.0.
7−-- A copy of the MPL can be found in the LICENSE file or
8−-- you can obtain it at http://mozilla.org/MPL/2.0/.
9−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
10−-- Stability : experimental
11−-- Portability : non-portable (GHC extensions)
12−
13−module Network.PagerDuty
14− ( withToken
15− , withBasicAuth
16− , unAuthenticated
17− , module Network.PagerDuty.API
18− )
19−where
20−
21−import Control.Monad.Reader
22−import Data.Monoid
23−import Network.HTTP.Client
24−import Network.HTTP.Client.TLS
25−import Network.PagerDuty.API
26−import Network.PagerDuty.Types
27−
28−
29−withToken :: MonadIO m
30− => Token
31− -> SubDomain
32− -> PagerDuty (Authenticated Token) b
33− -> m b
34−withToken tok sd pd = run pd (TokenEnv (addr sd) tok)
35−
36−withBasicAuth :: MonadIO m
37− => BasicAuth
38− -> SubDomain
39− -> PagerDuty (Authenticated BasicAuth) b
40− -> m b
41−withBasicAuth bas sd pd = run pd (BasicEnv (addr sd) bas)
42−
43−unAuthenticated :: MonadIO m => PagerDuty UnAuthenticated b -> m b
44−unAuthenticated pd = run pd Env
45−
46−run :: MonadIO m => PagerDuty a b -> (Manager -> Env a) -> m b
47−run pd env = liftIO . withManager tlsManagerSettings $ runReaderT pd . env
48−
49−addr :: SubDomain -> Host
50−addr = (<> ".pagerduty.com") . subDomain
src/Network/PagerDuty/API.hsdeleted−30
@@ -1,30 +0,0 @@
1−-- Module : Network.PagerDuty.API
2−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
3−-- License : This Source Code Form is subject to the terms of
4−-- the Mozilla Public License, v. 2.0.
5−-- A copy of the MPL can be found in the LICENSE file or
6−-- you can obtain it at http://mozilla.org/MPL/2.0/.
7−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
8−-- Stability : experimental
9−-- Portability : non-portable (GHC extensions)
10−
11−-- | Re-exports of the supported PagerDuty APIs
12−module Network.PagerDuty.API
13− ( -- * Supported APIs
14− module Network.PagerDuty.API.Integration
15− , module Network.PagerDuty.API.MaintenanceWindows
16− , module Network.PagerDuty.API.Services
17− -- * Common Types
18− , PagerDuty
19− , SubDomain (..)
20− , Token (..)
21− , BasicAuth (..)
22− , Code (..)
23− , Error (..)
24− )
25−where
26−
27−import Network.PagerDuty.API.Integration
28−import Network.PagerDuty.API.MaintenanceWindows
29−import Network.PagerDuty.API.Services
30−import Network.PagerDuty.Types
src/Network/PagerDuty/API/Integration.hsdeleted−80
@@ -1,80 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−
5−-- Module : Network.PagerDuty.Integration
6−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
7−-- License : This Source Code Form is subject to the terms of
8−-- the Mozilla Public License, v. 2.0.
9−-- A copy of the MPL can be found in the LICENSE file or
10−-- you can obtain it at http://mozilla.org/MPL/2.0/.
11−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
12−-- Stability : experimental
13−-- Portability : non-portable (GHC extensions)
14−
15−-- | The "Integration API"
16−--
17−-- <http://developer.pagerduty.com/documentation/integration/events>
18−--
19−module Network.PagerDuty.API.Integration
20− ( Event (..)
21− , submitEvent
22−
23− -- * re-exports
24− , ServiceKey
25− , IncidentKey
26− )
27−where
28−
29−import Control.Applicative
30−import Data.Aeson
31−import Data.Maybe (catMaybes)
32−import Data.Text (Text)
33−import GHC.Generics
34−import Network.PagerDuty.IO
35−import Network.PagerDuty.Types
36−
37−
38−submitEvent :: Event -> PagerDuty a (Either Error IncidentKey)
39−submitEvent = fmap (fmap incident_key) . request defaultRequest
40− { method = methodPost
41− , host = "events.pagerduty.com"
42− , path = "/generic/2010-04-15/create_event.json"
43− }
44−
45−
46−data Event
47− = Trigger !ServiceKey !(Maybe IncidentKey) !Text !(Maybe Object)
48− | Acknowledge !ServiceKey !IncidentKey !(Maybe Text) !(Maybe Object)
49− | Resolve !ServiceKey !IncidentKey !(Maybe Text) !(Maybe Object)
50− deriving (Eq, Show)
51−
52−instance ToJSON Event where
53− toJSON (Trigger svc minc desc mdets) =
54− jsonEvent "trigger" svc minc (Just desc) mdets
55− toJSON (Acknowledge svc inc mdesc mdets) =
56− jsonEvent "acknowledge" svc (Just inc) mdesc mdets
57− toJSON (Resolve svc inc mdesc mdets) =
58− jsonEvent "resolve" svc (Just inc) mdesc mdets
59−
60−jsonEvent :: Text
61− -> ServiceKey
62− -> Maybe IncidentKey
63− -> Maybe Text
64− -> Maybe Object
65− -> Value
66−jsonEvent typ skey ikey desc dets = object $ catMaybes
67− [ Just $ "event_type" .= typ
68− , Just $ "service_key" .= skey
69− , ("incident_key" .=) <$> ikey
70− , ("description" .=) <$> desc
71− , ("details" .=) <$> dets
72− ]
73−
74−data EventResponse = ER
75− { status :: !Text
76− , incident_key :: !IncidentKey
77− , message :: !Text
78− } deriving (Show, Generic)
79−
80−instance FromJSON EventResponse
src/Network/PagerDuty/API/LogEntries.hsdeleted−30
@@ -1,30 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−-- Module : Network.PagerDuty.API.LogEntries
4−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
5−-- License : This Source Code Form is subject to the terms of
6−-- the Mozilla Public License, v. 2.0.
7−-- A copy of the MPL can be found in the LICENSE file or
8−-- you can obtain it at http://mozilla.org/MPL/2.0/.
9−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
10−-- Stability : experimental
11−-- Portability : non-portable (GHC extensions)
12−
13−module Network.PagerDuty.API.LogEntries
14−where
15−
16−import Data.Aeson
17−import GHC.Generics
18−import Network.PagerDuty.Types
19−
20−
21−data LogEntries = LogEntries
22− { lesTotal :: !Int
23− , leslogEntries :: [LogEntry]
24− } deriving (Eq, Show, Generic)
25−
26−instance FromJSON LogEntries
27−instance ToJSON LogEntries
28−
29−logEntries :: PagerDuty Authenticated (Either Error LogEntries)
30−logEntries = undefined
src/Network/PagerDuty/API/MaintenanceWindows.hsdeleted−238
@@ -1,238 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−
4−-- Module : Network.PagerDuty.API.MaintenanceWindows
5−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
6−-- License : This Source Code Form is subject to the terms of
7−-- the Mozilla Public License, v. 2.0.
8−-- A copy of the MPL can be found in the LICENSE file or
9−-- you can obtain it at http://mozilla.org/MPL/2.0/.
10−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
11−-- Stability : experimental
12−-- Portability : non-portable (GHC extensions)
13−
14−module Network.PagerDuty.API.MaintenanceWindows
15− ( CreateMaintenanceWindow (..)
16− , MaintenanceWindowCounts (..)
17− , MaintenanceWindowCreatorDetails (..)
18− , MaintenanceWindowCreatorId
19− , MaintenanceWindowDetails (..)
20− , MaintenanceWindowFilter (..)
21− , MaintenanceWindowId
22− , ServiceInfo (..)
23− , UpdateMaintenanceWindow (..)
24−
25− , createMaintenanceWindow
26− , createMaintenanceWindow'
27− , updateMaintenanceWindow
28− , listMaintenanceWindows
29− , getMaintenanceWindow
30− , deleteMaintenaceWindow
31− )
32−where
33−
34−import Control.Applicative
35−import Data.Aeson
36−import Data.ByteString (ByteString)
37−import Data.Maybe
38−import Data.Monoid
39−import Data.Text (Text)
40−import qualified Data.Text as T
41−import Data.Text.Encoding
42−import Data.Time (UTCTime)
43−import GHC.Generics
44−import Network.PagerDuty.Internal
45−import Network.PagerDuty.IO
46−import Network.PagerDuty.Types
47−
48−
49−type A a = Authenticated a
50−
51−bASE_PATH :: ByteString
52−bASE_PATH = "/api/v1/maintenance_windows/"
53−
54−createMaintenanceWindow :: CreateMaintenanceWindow
55− -> RequesterId
56− -> PagerDuty (A Token) (Either Error MaintenanceWindowDetails)
57−createMaintenanceWindow mw rid =
58− fmap (fmap dr_maintenance_window)
59− . request defaultRequest
60− { method = methodPost
61− , path = bASE_PATH
62− }
63− $ CreateMaintenanceWindowRequest
64− { cmwr_maintenance_window = mw
65− , cmwr_requester_id = Just rid
66− }
67−
68−
69−createMaintenanceWindow' :: CreateMaintenanceWindow
70− -> PagerDuty (A BasicAuth) (Either Error MaintenanceWindowDetails)
71−createMaintenanceWindow' mw =
72− fmap (fmap dr_maintenance_window)
73− . request defaultRequest
74− { method = methodPost
75− , path = bASE_PATH
76− }
77− $ CreateMaintenanceWindowRequest
78− { cmwr_maintenance_window = mw
79− , cmwr_requester_id = Nothing
80− }
81−
82−
83−updateMaintenanceWindow :: MaintenanceWindowId
84− -> UpdateMaintenanceWindow
85− -> PagerDuty (A a) (Either Error MaintenanceWindowDetails)
86−updateMaintenanceWindow (Id mwid) =
87− fmap (fmap dr_maintenance_window)
88− . request defaultRequest
89− { method = methodPut
90− , path = bASE_PATH <> encodeUtf8 mwid
91− }
92−
93−
94−listMaintenanceWindows :: Maybe Text
95− -> [ServiceId]
96− -> Maybe MaintenanceWindowFilter
97− -> PagerDuty (A a) (Either Error (MaintenanceWindowCounts, [MaintenanceWindowDetails]))
98−listMaintenanceWindows qry sids filt =
99− fmap (fmap (liftA2 (,) lr_counts lr_maintenance_windows))
100− . request defaultRequest
101− { method = methodGet
102− , path = bASE_PATH
103− , queryString = renderSimpleQuery True mkQuery
104− }
105− $ T.empty
106− where
107− mkQuery :: [(ByteString, ByteString)]
108− mkQuery = (catMaybes [ fmap ((,) "query" . encodeUtf8) qry
109− , fmap ((,) "filter" . filterToBS) filt
110− ])
111− ++ map (\ (Id sid) -> ("service_ids[]", encodeUtf8 sid)) sids
112−
113−getMaintenanceWindow :: MaintenanceWindowId
114− -> PagerDuty (A a) (Either Error MaintenanceWindowDetails)
115−getMaintenanceWindow (Id mwid) =
116− fmap (fmap dr_maintenance_window)
117− . request defaultRequest
118− { method = methodGet
119− , path = bASE_PATH <> encodeUtf8 mwid
120− }
121− $ T.empty
122−
123−deleteMaintenaceWindow :: MaintenanceWindowId
124− -> PagerDuty (A a) (Either Error Empty)
125−deleteMaintenaceWindow (Id mwid) = request defaultRequest
126− { method = methodDelete
127− , path = bASE_PATH <> encodeUtf8 mwid
128− } $ T.empty
129−
130−data MaintenanceWindow
131−data MaintenanceWindowCreator
132−
133−type MaintenanceWindowId = Id MaintenanceWindow
134−type MaintenanceWindowCreatorId = Id MaintenanceWindowCreator
135−
136−
137−data CreateMaintenanceWindow = CreateMaintenanceWindow
138− { cmw_start_time :: !UTCTime
139− , cmw_end_time :: !UTCTime
140− , cmw_description :: Maybe Text
141− , cmw_service_ids :: [ServiceId]
142− } deriving (Show, Generic)
143−
144−instance ToJSON CreateMaintenanceWindow where toJSON = gToJson "cmw_"
145−
146−
147−data UpdateMaintenanceWindow = UpdateMaintenanceWindow
148− { umw_start_time :: Maybe UTCTime
149− , umw_end_time :: Maybe UTCTime
150− , umw_description :: Maybe Text
151− , umw_service_ids :: [ServiceId]
152− } deriving (Show, Generic)
153−
154−instance ToJSON UpdateMaintenanceWindow where toJSON = gToJson "umw_"
155−
156−
157−data CreateMaintenanceWindowRequest = CreateMaintenanceWindowRequest
158− { cmwr_maintenance_window :: !CreateMaintenanceWindow
159− , cmwr_requester_id :: Maybe RequesterId
160− } deriving (Show, Generic)
161−
162−instance ToJSON CreateMaintenanceWindowRequest where toJSON = gToJson "cmwr_"
163−
164−
165−data MaintenanceWindowDetails = MaintenanceWindowDetails
166− { mwd_id :: !MaintenanceWindowId
167− , mwd_sequence_number :: !Int
168− , mwd_start_time :: !UTCTime
169− , mwd_end_time :: !UTCTime
170− , mwd_description :: Maybe Text
171− , mwd_created_by :: !MaintenanceWindowCreatorDetails
172− , mwd_services :: [ServiceInfo]
173− } deriving (Show, Generic)
174−
175−instance FromJSON MaintenanceWindowDetails where parseJSON = gFromJson "mwd_"
176−
177−
178−-- possibly used elsewhere (UserDetails or somesuch)
179−data MaintenanceWindowCreatorDetails = MaintenanceWindowCreatorDetails
180− { mwcd_id :: !MaintenanceWindowCreatorId
181− , mwcd_name :: !Text
182− , mwcd_email :: !Text
183− , mwcd_time_zone :: !Text
184− , mwcd_color :: !Text -- wtf?
185− , mwcd_role :: !Text
186− , mwcd_avatar_url :: !Text
187− , mwcd_user_url :: !Text
188− , mwcd_invitation_sent :: !Bool
189− } deriving (Show, Generic)
190−
191−instance FromJSON MaintenanceWindowCreatorDetails where parseJSON = gFromJson "mwcd_"
192−
193−
194−data ServiceInfo = ServiceInfo
195− { si_name :: !Text
196− , si_url :: !Text
197− , si_id :: !ServiceId
198− } deriving (Show, Generic)
199−
200−instance FromJSON ServiceInfo where parseJSON = gFromJson "si_"
201−
202−
203−data ListResponse = ListResponse
204− { lr_maintenance_windows :: [MaintenanceWindowDetails]
205− , lr_query :: Maybe Text
206− , lr_counts :: !MaintenanceWindowCounts
207− } deriving (Show, Generic)
208−
209−instance FromJSON ListResponse where parseJSON = gFromJson "lr_"
210−
211−
212−data MaintenanceWindowCounts = MaintenanceWindowCounts
213− { mwc_ongoing :: !Int
214− , mwc_future :: !Int
215− , mwc_past :: !Int
216− , mwc_all :: !Int
217− } deriving (Show, Generic)
218−
219−instance FromJSON MaintenanceWindowCounts where parseJSON = gFromJson "mwc_"
220−
221−
222−newtype DetailsResponse = DetailsResponse
223− { dr_maintenance_window :: MaintenanceWindowDetails
224− } deriving (Show, Generic)
225−
226−instance FromJSON DetailsResponse where parseJSON = gFromJson "dr_"
227−
228−
229−data MaintenanceWindowFilter
230− = Past
231− | Future
232− | Ongoing
233− deriving Show
234−
235−filterToBS :: MaintenanceWindowFilter -> ByteString
236−filterToBS Past = "past"
237−filterToBS Future = "future"
238−filterToBS Ongoing = "ongoing"
src/Network/PagerDuty/API/Services.hsdeleted−302
@@ -1,302 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−
5−-- Module : Network.PagerDuty.API.Services
6−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
7−-- License : This Source Code Form is subject to the terms of
8−-- the Mozilla Public License, v. 2.0.
9−-- A copy of the MPL can be found in the LICENSE file or
10−-- you can obtain it at http://mozilla.org/MPL/2.0/.
11−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
12−-- Stability : experimental
13−-- Portability : non-portable (GHC extensions)
14−
15−module Network.PagerDuty.API.Services
16− ( ServiceDetails (..)
17− , CreateService (..)
18− , UpdateService (..)
19− , ServiceStatus (..)
20− , ServiceType (..)
21− , EmailFilterMode (..)
22− , IncidentCounts (..)
23− , EmailIncidentCreation (..)
24−
25− , createService
26− , updateService
27− , listServices
28− , getService
29− , deleteService
30− , disableService
31− , disableService'
32− , enableService
33− , enableService'
34− , regenerateServiceKey
35− )
36−where
37−
38−import Control.Applicative
39−import Control.Monad
40−import Data.Aeson
41−import Data.ByteString (ByteString)
42−import Data.Monoid
43−import Data.Text (Text)
44−import qualified Data.Text as T
45−import Data.Text.Encoding
46−import Data.Time
47−import GHC.Generics
48−import Network.PagerDuty.Internal
49−import Network.PagerDuty.IO
50−import Network.PagerDuty.Types
51−
52−
53−type A a = Authenticated a
54−
55−bASE_PATH :: ByteString
56−bASE_PATH = "/api/v1/services/"
57−
58−createService :: CreateService -> PagerDuty (A a) (Either Error ServiceDetails)
59−createService = fmap (fmap service) . request defaultRequest
60− { method = methodPost
61− , path = bASE_PATH
62− }
63− . CreateServiceRequest
64−
65−updateService :: ServiceId
66− -> UpdateService
67− -> PagerDuty (A a) (Either Error ServiceDetails)
68−updateService (Id sid) = fmap (fmap service) . request defaultRequest
69− { method = methodPut
70− , path = bASE_PATH <> encodeUtf8 sid
71− }
72− . UpdateServiceRequest
73−
74−listServices :: PagerDuty (A a) (Either Error [ServiceDetails])
75−listServices = fmap (fmap services) . request defaultRequest
76− { method = methodGet
77− , path = bASE_PATH
78− , queryString = "?include[]=escalation_policy&include[]=email_filters"
79− } $ T.empty
80−
81−getService :: ServiceId -> PagerDuty (A a) (Either Error ServiceDetails)
82−getService (Id sid) = fmap (fmap service) . request defaultRequest
83− { method = methodGet
84− , path = bASE_PATH <> encodeUtf8 sid
85− , queryString = "?include[]=escalation_policy&include[]=email_filters"
86− } $ T.empty
87−
88−deleteService :: ServiceId -> PagerDuty (A a) (Either Error Empty)
89−deleteService (Id sid) = request defaultRequest
90− { method = methodDelete
91− , path = bASE_PATH <> encodeUtf8 sid
92− } $ T.empty
93−
94−disableService :: ServiceId
95− -> RequesterId
96− -> PagerDuty (A Token) (Either Error Empty)
97−disableService (Id sid) (Id rid) = request defaultRequest
98− { method = methodPut
99− , path = bASE_PATH <> encodeUtf8 sid <> "/disable"
100− , queryString = "?requester_id=" <> encodeUtf8 rid
101− } $ T.empty
102−
103−disableService' :: ServiceId -> PagerDuty (A BasicAuth) (Either Error Empty)
104−disableService' (Id sid) = request defaultRequest
105− { method = methodPut
106− , path = bASE_PATH <> encodeUtf8 sid <> "/disable"
107− } $ T.empty
108−
109−enableService :: ServiceId
110− -> RequesterId
111− -> PagerDuty (A Token) (Either Error Empty)
112−enableService (Id sid) (Id rid) = request defaultRequest
113− { method = methodPut
114− , path = bASE_PATH <> encodeUtf8 sid <> "/enable"
115− , queryString = "?requester_id=" <> encodeUtf8 rid
116− } $ T.empty
117−
118−enableService' :: ServiceId -> PagerDuty (A BasicAuth) (Either Error Empty)
119−enableService' (Id sid) = request defaultRequest
120− { method = methodPut
121− , path = bASE_PATH <> encodeUtf8 sid <> "/enable"
122− } $ T.empty
123−
124−regenerateServiceKey :: ServiceId -> PagerDuty (A a) (Either Error ServiceKey)
125−regenerateServiceKey (Id sid) = fmap (fmap (sd_service_key . service)) . request defaultRequest
126− { method = methodPost
127− , path = bASE_PATH <> encodeUtf8 sid <> "/regenerate_key"
128− } $ T.empty
129−
130−
131−newtype CreateServiceRequest = CreateServiceRequest
132− { csr_service :: CreateService
133− } deriving (Show, Generic)
134−
135−instance ToJSON CreateServiceRequest where toJSON = gToJson "csr_"
136−
137−
138−newtype UpdateServiceRequest = UpdateServiceRequest
139− { usr_service :: UpdateService
140− } deriving (Show, Generic)
141−
142−instance ToJSON UpdateServiceRequest where toJSON = gToJson "usr_"
143−
144−
145−newtype DetailsListResponse = DLR { services :: [ServiceDetails] }
146− deriving (Show, Generic)
147−
148−instance FromJSON DetailsListResponse
149−
150−
151−newtype DetailsResponse = DR { service :: ServiceDetails }
152− deriving (Show, Generic)
153−
154−instance FromJSON DetailsResponse
155−
156−
157−data CreateService = CreateService
158− { cs_name :: !Text
159− , cs_escalation_policy_id :: !Text
160− , cs_type :: !ServiceType
161− , cs_description :: Maybe Text
162− , cs_acknowledgement_timeout :: Maybe Int
163− , cs_auto_resolve_timeout :: Maybe Int
164− , cs_severity_filter :: Maybe Text
165− } deriving (Show, Generic)
166−
167−instance ToJSON CreateService where toJSON = gToJson "cs_"
168−
169−
170−data UpdateService = UpdateService
171− { us_name :: Maybe Text
172− , us_escalation_policy_id :: Maybe Text
173− , us_type :: Maybe ServiceType
174− , us_description :: Maybe Text
175− , us_acknowledgement_timeout :: Maybe Int
176− , us_auto_resolve_timeout :: Maybe Int
177− , us_severity_filter :: Maybe Text
178− } deriving (Show, Generic)
179−
180−instance ToJSON UpdateService where toJSON = gToJson "us_"
181−
182−
183−data ServiceDetails = ServiceDetails
184− { sd_id :: !ServiceId
185− , sd_name :: !Text
186− , sd_description :: !Text
187− , sd_service_url :: !Text
188− , sd_service_key :: !ServiceKey
189− , sd_auto_resolve_timeout :: !Int
190− , sd_acknowledgement_timeout :: !Int
191− , sd_created_at :: !ZonedTime
192− , sd_deleted_at :: Maybe ZonedTime
193− , sd_status :: !ServiceStatus
194− , sd_last_incident_timestamp :: Maybe ZonedTime
195− , sd_email_incident_creation :: Maybe EmailIncidentCreation
196− , sd_incident_counts :: !IncidentCounts
197− , sd_email_filter_mode :: !EmailFilterMode
198− , sd_type :: !ServiceType
199− , sd_escalation_policy :: Maybe Object -- todo
200− , sd_email_filters :: Maybe Object -- todo
201− , sd_severity_filter :: Maybe Text -- todo?
202− } deriving (Show, Generic)
203−
204−instance FromJSON ServiceDetails where parseJSON = gFromJson "sd_"
205−
206−data ServiceStatus
207− = Active
208− | Warning
209− | Critical
210− | Maintenance
211− | Disabled
212− deriving (Eq, Show)
213−
214−instance FromJSON ServiceStatus where
215− parseJSON (String s) = case s of
216− "active" -> pure Active
217− "warning" -> pure Warning
218− "critical" -> pure Critical
219− "maintenance" -> pure Maintenance
220− "disabled" -> pure Disabled
221− _ -> mzero
222−
223− parseJSON _ = mzero
224−
225−
226−data IncidentCounts = IncidentCounts
227− { triggered :: !Int
228− , acknowledged :: !Int
229− , resolved :: !Int
230− , total :: !Int
231− } deriving (Show, Generic)
232−
233−instance FromJSON IncidentCounts
234−
235−
236−data EmailIncidentCreation
237− = OnNewEmail
238− | OnNewEmailSubject
239− | OnlyIfNoOpenIncidents
240− deriving (Eq, Show)
241−
242−instance FromJSON EmailIncidentCreation where
243− parseJSON (String s) = case s of
244− "on_new_email" -> pure OnNewEmail
245− "on_new_email_subject" -> pure OnNewEmailSubject
246− "only_if_no_open_incidents" -> pure OnlyIfNoOpenIncidents
247− _ -> mzero
248−
249− parseJSON _ = mzero
250−
251−
252−data EmailFilterMode
253− = AllEmail
254− | OrRulesEmail
255− | AndRulesEmail
256− deriving (Eq, Show)
257−
258−instance FromJSON EmailFilterMode where
259− parseJSON (String s) = case s of
260− "all-email" -> pure AllEmail
261− "or-rules-email" -> pure OrRulesEmail
262− "and-rules-email" -> pure AndRulesEmail
263− _ -> mzero
264−
265− parseJSON _ = mzero
266−
267−
268−data ServiceType
269− = CloudKick
270− | GenericEmail
271− | GenericEventsApi
272− | Keynote
273− | Nagios
274− | Pingdom
275− | ServerDensity
276− | SqlMonitor
277− deriving (Eq, Show)
278−
279−instance FromJSON ServiceType where
280− parseJSON (String s) = case s of
281− "cloudkick" -> pure CloudKick
282− "generic_email" -> pure GenericEmail
283− "generic_events_api" -> pure GenericEventsApi
284− "keynote" -> pure Keynote
285− "nagios" -> pure Nagios
286− "pingdom" -> pure Pingdom
287− "server_density" -> pure ServerDensity
288− "sql_monitor" -> pure SqlMonitor
289− _ -> mzero
290−
291− parseJSON _ = mzero
292−
293−instance ToJSON ServiceType where
294− toJSON st = String $ case st of
295− CloudKick -> "cloudkick"
296− GenericEmail -> "generic_email"
297− GenericEventsApi -> "generic_events_api"
298− Keynote -> "keynote"
299− Nagios -> "nagios"
300− Pingdom -> "pingdom"
301− ServerDensity -> "server_density"
302− SqlMonitor -> "sql_monitor"
src/Network/PagerDuty/IO.hsdeleted−78
@@ -1,78 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−-- Module : Network.PagerDuty.IO
4−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
5−-- License : This Source Code Form is subject to the terms of
6−-- the Mozilla Public License, v. 2.0.
7−-- A copy of the MPL can be found in the LICENSE file or
8−-- you can obtain it at http://mozilla.org/MPL/2.0/.
9−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
10−-- Stability : experimental
11−-- Portability : non-portable (GHC extensions)
12−
13−module Network.PagerDuty.IO
14− ( request
15− , defaultRequest
16− , Request (..)
17− , module HTTPTypes
18− )
19−where
20−
21−import Control.Applicative
22−import Control.Monad.Reader
23−import Data.Aeson
24−import qualified Data.ByteString.Lazy as L
25−import Data.Default
26−import Data.Maybe
27−import Data.Monoid
28−import Network.HTTP.Client
29−import Network.HTTP.Types as HTTPTypes
30−import Network.PagerDuty.Types
31−
32−
33−defaultRequest :: Request
34−defaultRequest = def
35−
36−request :: (ToJSON a, FromJSON b) => Request -> a -> PagerDuty x (Either Error b)
37−request rq rqBody = liftIO . go =<< ask
38− where
39− go env = httpLbs req mgr >>= response
40− where
41− (req,mgr) = case env of
42− TokenEnv h tok m -> (authToken tok . rq' $ Just h, m)
43− BasicEnv h bas m -> (authBasic bas . rq' $ Just h, m)
44− Env m -> (rq' Nothing, m)
45−
46− rq' h = rq { secure = True
47− , host = fromMaybe (host rq) h
48− , port = 443
49− , requestHeaders = [ ("Accept", "application/json")
50− , ("Content-Type", "application/json")
51− ]
52− , requestBody = RequestBodyLBS $ encode rqBody
53− }
54−
55− authToken (Token t) req =
56− req { requestHeaders = ("Authorization", "Token token=" <> t)
57− : requestHeaders req }
58−
59− authBasic (BasicAuth u p) = applyBasicAuth u p
60−
61−response :: FromJSON a => Response L.ByteString -> IO (Either Error a)
62−response res = case statusCode (responseStatus res) of
63− 200 -> success
64− 201 -> success
65− 400 -> failure
66− 500 -> failure
67− n -> return . Left . Internal $
68− "PagerDuty returned unhandled status code: " ++ show n
69− where
70− success = maybe (Left unknown) Right <$> parse
71− failure = maybe (Left unknown) Left <$> parse
72−
73− unknown = Internal
74− $ "Unable to parse response into a PagerDuty API compatible type: "
75− ++ show (responseBody res)
76−
77− parse :: FromJSON a => IO (Maybe a)
78− parse = pure . decode . responseBody $ res
src/Network/PagerDuty/Integration.hsnew file+292
@@ -0,0 +1,292 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE FunctionalDependencies #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE OverloadedStrings #-}
6+{-# LANGUAGE TemplateHaskell #-}
7+
8+-- Module : Network.PagerDuty.Integration
9+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
10+-- License : This Source Code Form is subject to the terms of
11+-- the Mozilla Public License, v. 2.0.
12+-- A copy of the MPL can be found in the LICENSE file or
13+-- you can obtain it at http://mozilla.org/MPL/2.0/.
14+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
15+-- Stability : experimental
16+-- Portability : non-portable (GHC extensions)
17+
18+-- | The PagerDuty event integration API is how you would add PagerDuty's
19+-- advanced alerting functionality to any system that can make an HTTP API
20+-- call. You can now add phone, SMS and email alerting to your monitoring
21+-- tools, ticketing systems and custom software.
22+--
23+-- The API was designed to allow you to easily integrate a
24+-- monitoring system with a Service in PagerDuty. Monitoring systems
25+-- generally send out events when problems are detected and when these
26+-- problems have been resolved (fixed). Some more advanced systems also
27+-- understand the concept of acknowledgements: problems can be acknowledged
28+-- by an engineer to signal he or she is working on fixing the issue.
29+--
30+-- Since monitoring systems emit events, the API is based around accepting
31+-- events. Incoming events (sent via the API) are routed to a PagerDuty
32+-- service and processed. They may result in a new incident being created,
33+-- or an existing incident being acknowledged or resolved.
34+--
35+-- The same event-based API can also be used to integrate a PagerDuty
36+-- service with ticketing systems and various other software tools.
37+--
38+-- /See:/ <http://developer.pagerduty.com/documentation/integration/events>
39+module Network.PagerDuty.Integration
40+ (
41+ -- * Sending events
42+ submit
43+ , submitWith
44+
45+ -- ** Trigger
46+ , Trigger
47+ , trigger
48+ , client
49+ , clientUrl
50+
51+ -- ** Acknowledge
52+ , Acknowledge
53+ , acknowledge
54+
55+ -- ** Resolve
56+ , Resolve
57+ , resolve
58+
59+ -- * Event fields
60+ , HasServiceKey (..)
61+ , HasIncidentKey (..)
62+ , HasDescription (..)
63+ , HasDetails (..)
64+
65+ -- * Types
66+ , Event (..)
67+ , _Trigger
68+ , _Acknowledge
69+ , _Resolve
70+
71+ , Response
72+ , rsStatus
73+ , rsMessage
74+ , rsIncidentKey
75+ ) where
76+
77+import Control.Lens
78+import Control.Monad.IO.Class
79+import Data.Aeson
80+import Data.Default.Class
81+import qualified Data.HashMap.Strict as Map
82+import Data.Text (Text)
83+import Network.HTTP.Client (Manager)
84+import qualified Network.HTTP.Client as Client
85+import Network.PagerDuty.Internal.IO
86+import Network.PagerDuty.Internal.TH
87+import Network.PagerDuty.Internal.Types
88+
89+data Response = Response
90+ { _rsStatus :: Text
91+ , _rsMessage :: Text
92+ , _rsIncidentKey :: IncidentKey
93+ } deriving (Eq, Show)
94+
95+deriveJSON ''Response
96+
97+-- | @success@.
98+makeLens "_rsStatus" ''Response
99+
100+-- | @Event processed@.
101+makeLens "_rsMessage" ''Response
102+
103+-- | The key of the incident that will be affected by the request.
104+makeLens "_rsIncidentKey" ''Response
105+
106+class HasServiceKey s a | s -> a where
107+ -- | The GUID of one of your "Generic API" services. This is the "service key"
108+ -- listed on a Generic API's service detail page.
109+ serviceKey :: Lens' s a
110+
111+class HasIncidentKey s a | s -> a where
112+ -- | Identifies the incident to which this trigger event should be applied.
113+ -- If there's no open (i.e. unresolved) incident with this key, a new one
114+ -- will be created. If there's already an open incident with a matching key,
115+ -- this event will be appended to that incident's log. The event key provides
116+ -- an easy way to "de-dup" problem reports. If this field isn't provided,
117+ -- PagerDuty will automatically open a new incident with a unique key.
118+ incidentKey :: Lens' s a
119+
120+class HasDescription s a | s -> a where
121+ -- | A short description of the problem that led to this trigger.
122+ -- This field (or a truncated version) will be used when generating phone
123+ -- calls, SMS messages and alert emails. It will also appear on the incidents
124+ -- tables in the PagerDuty UI. The maximum length is 1024 characters.
125+ description :: Lens' s a
126+
127+class HasDetails s a | s -> a where
128+ -- | An arbitrary JSON object containing any data you'd like included
129+ -- in the incident log.
130+ details :: Lens' s a
131+
132+data Trigger = Trigger'
133+ { _tServiceKey' :: ServiceKey
134+ , _tIncidentKey' :: IncidentKey
135+ , _tDescription' :: Text
136+ , _tDetails' :: Maybe Object
137+ , _tClient' :: Maybe Text
138+ , _tClientUrl' :: Maybe Text
139+ } deriving (Eq, Show)
140+
141+deriveRecord ''Trigger
142+
143+instance HasServiceKey Trigger ServiceKey where serviceKey = tServiceKey'
144+instance HasIncidentKey Trigger IncidentKey where incidentKey = tIncidentKey'
145+instance HasDescription Trigger Text where description = tDescription'
146+instance HasDetails Trigger (Maybe Object) where details = tDetails'
147+
148+-- | The name of the monitoring client that is triggering this event.
149+client :: Lens' Trigger (Maybe Text)
150+client = tClient'
151+
152+-- | The URL of the monitoring client that is triggering this event.
153+clientUrl :: Lens' Trigger (Maybe Text)
154+clientUrl = tClientUrl'
155+
156+data Generic = Generic'
157+ { _gServiceKey' :: ServiceKey
158+ , _gIncidentKey' :: IncidentKey
159+ , _gDescription' :: Maybe Text
160+ , _gDetails' :: Maybe Object
161+ } deriving (Eq, Show)
162+
163+deriveRecord ''Generic
164+
165+instance HasServiceKey Generic ServiceKey where serviceKey = gServiceKey'
166+instance HasIncidentKey Generic IncidentKey where incidentKey = gIncidentKey'
167+instance HasDescription Generic (Maybe Text) where description = gDescription'
168+instance HasDetails Generic (Maybe Object) where details = gDetails'
169+
170+type Resolve = Generic
171+type Acknowledge = Generic
172+
173+data Event
174+ = Trigger Trigger -- ^ /See:/ 'trigger'
175+ | Acknowledge Generic -- ^ /See:/ 'acknowledge'
176+ | Resolve Generic -- ^ /See:/ 'resolve'
177+ deriving (Eq, Show)
178+
179+makePrisms ''Event
180+
181+instance HasServiceKey Event ServiceKey where
182+ serviceKey = lens f g
183+ where
184+ f (Trigger s) = _tServiceKey' s
185+ f (Acknowledge s) = _gServiceKey' s
186+ f (Resolve s) = _gServiceKey' s
187+
188+ g (Trigger s) x = Trigger $ s { _tServiceKey' = x }
189+ g (Acknowledge s) x = Acknowledge $ s { _gServiceKey' = x }
190+ g (Resolve s) x = Resolve $ s { _gServiceKey' = x }
191+
192+instance HasIncidentKey Event IncidentKey where
193+ incidentKey = lens f g
194+ where
195+ f (Trigger s) = _tIncidentKey' s
196+ f (Acknowledge s) = _gIncidentKey' s
197+ f (Resolve s) = _gIncidentKey' s
198+
199+ g (Trigger s) x = Trigger $ s { _tIncidentKey' = x }
200+ g (Acknowledge s) x = Acknowledge $ s { _gIncidentKey' = x }
201+ g (Resolve s) x = Resolve $ s { _gIncidentKey' = x }
202+
203+instance HasDetails Event (Maybe Object) where
204+ details = lens f g
205+ where
206+ f (Trigger s) = _tDetails' s
207+ f (Acknowledge s) = _gDetails' s
208+ f (Resolve s) = _gDetails' s
209+
210+ g (Trigger s) x = Trigger $ s { _tDetails' = x }
211+ g (Acknowledge s) x = Acknowledge $ s { _gDetails' = x }
212+ g (Resolve s) x = Resolve $ s { _gDetails' = x }
213+
214+instance ToJSON Event where
215+ toJSON = \case
216+ Trigger x -> event "trigger" x
217+ Acknowledge x -> event "acknowledge" x
218+ Resolve x -> event "resolve" x
219+ where
220+ event k x =
221+ case toJSON x of
222+ Object o -> Object (Map.insert "event_type" (String k) o)
223+ v -> v
224+
225+-- | Your monitoring tools should send PagerDuty a trigger event to report a new
226+-- or ongoing problem. When PagerDuty receives a trigger event, it will either open
227+-- a new incident, or add a new trigger log entry to an existing incident,
228+-- depending on the provided incident_key.
229+trigger :: ServiceKey -- ^ 'serviceKey'
230+ -> IncidentKey -- ^ 'incidentKey'
231+ -> Text -- ^ 'description'
232+ -> Event
233+trigger k i d =
234+ Trigger Trigger'
235+ { _tServiceKey' = k
236+ , _tDescription' = d
237+ , _tIncidentKey' = i
238+ , _tClient' = Nothing
239+ , _tClientUrl' = Nothing
240+ , _tDetails' = Nothing
241+ }
242+
243+-- | Acknowledge events cause the referenced incident to enter the acknowledged
244+-- state.
245+--
246+-- While an incident is acknowledged, it won't generate any additional
247+-- notifications, even if it receives new trigger events. Your monitoring tools
248+-- should send PagerDuty an acknowledge event when they know someone is
249+-- presently working on the problem.
250+acknowledge :: ServiceKey -- ^ 'serviceKey'
251+ -> IncidentKey -- ^ 'incidentKey'
252+ -> Event
253+acknowledge k i =
254+ Acknowledge Generic'
255+ { _gServiceKey' = k
256+ , _gIncidentKey' = i
257+ , _gDescription' = Nothing
258+ , _gDetails' = Nothing
259+ }
260+
261+-- | Resolve events cause the referenced incident to enter the resolved state.
262+--
263+-- Once an incident is resolved, it won't generate any additional
264+-- notifications. New trigger events with the same incident_key as a resolved
265+-- incident won't re-open the incident. Instead, a new incident will be
266+-- created. Your monitoring tools should send PagerDuty a resolve event when the
267+-- problem that caused the initial trigger event has been fixed.
268+resolve :: ServiceKey -- ^ 'serviceKey'
269+ -> IncidentKey -- ^ 'incidentKey'
270+ -> Event
271+resolve k i =
272+ Resolve Generic'
273+ { _gServiceKey' = k
274+ , _gIncidentKey' = i
275+ , _gDescription' = Nothing
276+ , _gDetails' = Nothing
277+ }
278+
279+-- | Send an event to the integration API.
280+submit :: MonadIO m => Manager -> Event -> m (Either Error Response)
281+submit m = submitWith m None
282+
283+-- | /See:/ 'submit'
284+submitWith :: MonadIO m
285+ => Manager
286+ -> Logger
287+ -> Event
288+ -> m (Either Error Response)
289+submitWith m l e = request m l e $
290+ def { Client.host = "events.pagerduty.com"
291+ , Client.path = "/generic/2010-04-15/create_event.json"
292+ }
src/Network/PagerDuty/Internal.hsdeleted−45
@@ -1,45 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE ViewPatterns #-}
4−
5−-- Module : Network.PagerDuty.Internal
6−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
7−-- License : This Source Code Form is subject to the terms of
8−-- the Mozilla Public License, v. 2.0.
9−-- A copy of the MPL can be found in the LICENSE file or
10−-- you can obtain it at http://mozilla.org/MPL/2.0/.
11−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
12−-- Stability : experimental
13−-- Portability : non-portable (GHC extensions)
14−
15−module Network.PagerDuty.Internal
16− ( gToJson
17− , gFromJson
18− , jsonKey
19− ) where
20−
21−import Data.Aeson.Types
22−import Data.Char (isUpper, toLower)
23−import Data.List
24−import GHC.Generics
25−
26−
27−gToJson :: (Generic a, GToJSON (Rep a)) => String -> a -> Value
28−gToJson prefix = genericToJSON defaultOptions
29− { fieldLabelModifier = jsonKey prefix
30− , omitNothingFields = True
31− }
32−
33−gFromJson :: (Generic a, GFromJSON (Rep a)) => String -> Value -> Parser a
34−gFromJson prefix = genericParseJSON defaultOptions
35− { fieldLabelModifier = jsonKey prefix }
36−
37−jsonKey :: String -> String -> String
38−jsonKey prefix str = case str of
39− (stripPrefix prefix -> Just cs) -> underscore cs
40− _ -> underscore str
41− where
42− underscore [] = []
43− underscore (!c : cs)
44− | isUpper c = '_' : underscore (toLower c : cs)
45− | otherwise = c : underscore cs
src/Network/PagerDuty/Internal/IO.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+
4+-- Module : Network.PagerDuty.Internal.IO
5+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
6+-- License : This Source Code Form is subject to the terms of
7+-- the Mozilla Public License, v. 2.0.
8+-- A copy of the MPL can be found in the LICENSE file or
9+-- you can obtain it at http://mozilla.org/MPL/2.0/.
10+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
11+-- Stability : experimental
12+-- Portability : non-portable (GHC extensions)
13+
14+module Network.PagerDuty.Internal.IO where
15+
16+import Control.Monad
17+import Control.Monad.IO.Class
18+import Data.Aeson hiding (Error)
19+import qualified Data.ByteString.Lazy as LBS
20+import Data.Monoid
21+import qualified Data.Text as Text
22+import Network.HTTP.Client (Manager, httpLbs)
23+import qualified Network.HTTP.Client as Client
24+import Network.HTTP.Types
25+import Network.PagerDuty.Internal.Types
26+
27+request :: (MonadIO m, ToJSON a, FromJSON b)
28+ => Manager
29+ -> Logger
30+ -> a
31+ -> Client.Request
32+ -> m (Either Error b)
33+request m l x rq = msg >> liftIO (httpLbs raw m) >>= response l
34+ where
35+ msg = debug l ("[Raw Request]\n" <> Text.pack (show raw))
36+
37+ raw = rq
38+ { Client.secure = True
39+ , Client.port = 443
40+ , Client.requestHeaders = headers
41+ , Client.requestBody = Client.RequestBodyLBS (encode x)
42+ }
43+
44+ headers =
45+ [ ("Content-Type", "application/json")
46+ , ("Accept", "application/json")
47+ ]
48+
49+response :: forall m b. (MonadIO m, FromJSON b)
50+ => Logger
51+ -> Client.Response LBS.ByteString
52+ -> m (Either Error b)
53+response l rs = case statusCode (Client.responseStatus rs) of
54+ 200 -> success
55+ 201 -> success
56+ 400 -> failure
57+ 500 -> failure
58+ n -> return . Left $ unhandled n
59+ where
60+ success = maybe (Left unknown) Right `liftM` parse
61+ failure = maybe (Left unknown) Left `liftM` parse
62+
63+ unhandled n = Internal $
64+ "PagerDuty returned unhandled status code: "
65+ <> Text.pack (show n)
66+
67+ unknown = Internal $
68+ "Unable to parse response into a PagerDuty API compatible type: "
69+ <> Text.pack (show body)
70+
71+ parse :: FromJSON a => m (Maybe a)
72+ parse = msg >> return (decode body)
73+
74+ msg = debug l ("[Raw Response]\n" <> Text.pack (show rs))
75+
76+ body = Client.responseBody rs
src/Network/PagerDuty/Internal/Options.hsnew file+56
@@ -0,0 +1,56 @@
1+-- Module : Network.PagerDuty.Internal.Options
2+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
3+-- License : This Source Code Form is subject to the terms of
4+-- the Mozilla Public License, v. 2.0.
5+-- A copy of the MPL can be found in the LICENSE file or
6+-- you can obtain it at http://mozilla.org/MPL/2.0/.
7+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
8+-- Stability : experimental
9+-- Portability : non-portable (GHC extensions)
10+
11+module Network.PagerDuty.Internal.Options
12+ ( dropped
13+ , hyphenated
14+ , underscored
15+ ) where
16+
17+import Data.Aeson.Types
18+import Data.Char
19+import Data.List
20+
21+dropped :: Int -> Options -> Options
22+dropped n o = o
23+ { constructorTagModifier = constructorTagModifier o . drop n
24+ }
25+
26+hyphenated :: Options
27+hyphenated = underscored
28+ { fieldLabelModifier = unsuffix . hyphenate . unprefix
29+ , constructorTagModifier = hyphenate
30+ }
31+
32+underscored :: Options
33+underscored = defaultOptions
34+ { fieldLabelModifier = unsuffix . underscore . unprefix
35+ , constructorTagModifier = underscore
36+ , omitNothingFields = True
37+ , allNullaryToStringTag = True
38+ }
39+
40+hyphenate :: String -> String
41+hyphenate = intercalate "-" . map lower . splitBy isUpper
42+
43+underscore :: String -> String
44+underscore = intercalate "_" . map lower . splitBy isUpper
45+
46+lower :: String -> String
47+lower = map toLower
48+
49+unsuffix :: String -> String
50+unsuffix = takeWhile (/= '\'')
51+
52+unprefix :: String -> String
53+unprefix = dropWhile (not . isUpper)
54+
55+splitBy :: (a -> Bool) -> [a] -> [[a]]
56+splitBy p = groupBy (const (not . p))
src/Network/PagerDuty/Internal/Query.hsnew file+109
@@ -0,0 +1,109 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE GADTs #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TupleSections #-}
7+
8+-- Module : Network.PagerDuty.Internal.Query
9+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
10+-- License : This Source Code Form is subject to the terms of
11+-- the Mozilla Public License, v. 2.0.
12+-- A copy of the MPL can be found in the LICENSE file or
13+-- you can obtain it at http://mozilla.org/MPL/2.0/.
14+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
15+-- Stability : experimental
16+-- Portability : non-portable (GHC extensions)
17+
18+module Network.PagerDuty.Internal.Query
19+ ( QueryValues (..)
20+ , gquery
21+ , gqueryWith
22+ ) where
23+
24+import Data.Aeson.Types
25+import Data.Bifunctor
26+import Data.ByteString (ByteString)
27+import qualified Data.ByteString.Char8 as BS
28+import Data.ByteString.Conversion
29+import Data.Function (on)
30+import Data.List (groupBy)
31+import Data.Monoid ((<>))
32+import Data.Text (Text)
33+import qualified Data.Text.Encoding as Text
34+import Generics.SOP
35+import Network.HTTP.Types
36+import Network.PagerDuty.Internal.Options
37+
38+class QueryValues a where
39+ queryValues :: a -> [ByteString]
40+
41+ default queryValues :: ToByteString a => a -> [ByteString]
42+ queryValues = (:[]) . toByteString'
43+
44+instance QueryValues a => QueryValues (Maybe a) where
45+ queryValues (Just x) = queryValues x
46+ queryValues Nothing = []
47+
48+instance QueryValues Text where
49+ queryValues x = [Text.encodeUtf8 x]
50+
51+gquery :: forall a. (Generic a, HasDatatypeInfo a, All2 QueryValues (Code a))
52+ => a
53+ -> Query
54+gquery = gqueryWith underscored
55+
56+gqueryWith :: forall a. (Generic a, HasDatatypeInfo a, All2 QueryValues (Code a))
57+ => Options
58+ -> a
59+ -> Query
60+gqueryWith o a = case datatypeInfo (Proxy :: Proxy a) of
61+ ADT _ _ cs -> go cs (from a)
62+ Newtype _ _ c -> go (c :* Nil) (from a)
63+ where
64+ go :: (All2 QueryValues xss, SingI xss)
65+ => NP ConstructorInfo xss
66+ -> SOP I xss
67+ -> Query
68+ go cs (SOP sop) = group . unI . hcollapse $ hcliftA2' p (gctor o) cs sop
69+
70+ group :: [(ByteString, ByteString)] -> Query
71+ group = concatMap f . groupBy ((==) `on` fst)
72+ where
73+ f [] = []
74+ f [(k, v)] = [(k, Just v)]
75+ f xs@((k,_):_) = let n = k <> "[]" in map (bimap (const n) Just) xs
76+
77+gctor :: All QueryValues xs
78+ => Options
79+ -> ConstructorInfo xs
80+ -> NP I xs
81+ -> K [(ByteString, ByteString)] xs
82+gctor o (Constructor n) args =
83+ K . concat . hcollapse $ hcliftA p (K . map (k,) . queryValues . unI) args
84+ where
85+ k = BS.pack (constructorTagModifier o n)
86+
87+gctor o (Record _ ns) args =
88+ K . concat . hcollapse $ hcliftA2 p (gfield o) ns args
89+
90+gctor o (Infix n _ _) (x :* y :* Nil) =
91+ K $ map (k,) (queryValues (unI x) ++ queryValues (unI y))
92+ where
93+ k = BS.pack (constructorTagModifier o n)
94+
95+gctor _ Infix{} _ =
96+ error "Network.PagerDuty.Generics.inaccessible"
97+
98+gfield :: QueryValues a
99+ => Options
100+ -> FieldInfo a
101+ -> I a
102+ -> K [(ByteString, ByteString)] a
103+gfield o (FieldInfo f) (I a) =
104+ K $ map (k,) (queryValues a)
105+ where
106+ k = BS.pack (fieldLabelModifier o f)
107+
108+p :: Proxy QueryValues
109+p = Proxy
src/Network/PagerDuty/Internal/TH.hsnew file+96
@@ -0,0 +1,96 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+-- Module : Network.PagerDuty.Internal.TH
4+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
5+-- License : This Source Code Form is subject to the terms of
6+-- the Mozilla Public License, v. 2.0.
7+-- A copy of the MPL can be found in the LICENSE file or
8+-- you can obtain it at http://mozilla.org/MPL/2.0/.
9+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
10+-- Stability : experimental
11+-- Portability : non-portable (GHC extensions)
12+
13+module Network.PagerDuty.Internal.TH
14+ (
15+ -- * Requests
16+ jsonRequest
17+ , queryRequest
18+
19+ -- * Compound expressions
20+ , deriveNullary
21+ , deriveNullaryWith
22+ , deriveRecord
23+
24+ -- * JSON
25+ , deriveJSON
26+ , deriveJSONWith
27+
28+ -- * Lenses
29+ , makeLens
30+ , makeFields
31+
32+ -- * Re-exported generics
33+ , deriveGeneric
34+
35+ -- * Re-exported options
36+ , dropped
37+ , hyphenated
38+ , underscored
39+ ) where
40+
41+import Control.Applicative
42+import Control.Lens
43+import qualified Data.Aeson.TH as Aeson
44+import Data.Aeson.Types
45+import Data.ByteString (ByteString)
46+import qualified Data.Text.Encoding as Text
47+import Generics.SOP.TH
48+import Language.Haskell.TH
49+import Network.HTTP.Types.QueryLike
50+import Network.PagerDuty.Internal.Query
51+import Network.PagerDuty.Internal.Options
52+
53+jsonRequest :: Name -> Q [Dec]
54+jsonRequest n = concat <$> sequence
55+ [ makeLenses n
56+ , deriveJSON n
57+ , [d|instance QueryLike $(conT n) where toQuery = const []|]
58+ ]
59+
60+queryRequest :: Name -> Q [Dec]
61+queryRequest n = concat <$> sequence
62+ [ deriveGeneric n
63+ , makeLenses n
64+ , [d|instance ToJSON $(conT n) where toJSON = const (toJSON (object []))|]
65+ , [d|instance QueryLike $(conT n) where toQuery = gquery|]
66+ ]
67+
68+deriveNullary :: Name -> Q [Dec]
69+deriveNullary = deriveNullaryWith underscored
70+
71+deriveNullaryWith :: Options -> Name -> Q [Dec]
72+deriveNullaryWith o n = concat <$> sequence
73+ [ deriveJSONWith o n
74+ , [d|instance QueryValues $(conT n) where queryValues = value . toJSON|]
75+ ]
76+
77+deriveRecord :: Name -> Q [Dec]
78+deriveRecord n = concat <$> sequence
79+ [ deriveJSON n
80+ , makeLenses n
81+ ]
82+
83+deriveJSON :: Name -> Q [Dec]
84+deriveJSON = deriveJSONWith underscored
85+
86+deriveJSONWith :: Options -> Name -> Q [Dec]
87+deriveJSONWith = Aeson.deriveJSON
88+
89+makeLens :: String -> Name -> Q [Dec]
90+makeLens k = makeLensesWith
91+ $ lensRulesFor [(k, drop 1 k)]
92+ & simpleLenses .~ True
93+
94+value :: Value -> [ByteString]
95+value (String t) = [Text.encodeUtf8 t]
96+value _ = []
src/Network/PagerDuty/Internal/Types.hsnew file+480
@@ -0,0 +1,480 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE KindSignatures #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PatternSynonyms #-}
11+{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE StandaloneDeriving #-}
13+{-# LANGUAGE TemplateHaskell #-}
14+{-# LANGUAGE TupleSections #-}
15+{-# LANGUAGE ViewPatterns #-}
16+
17+-- Module : Network.PagerDuty.Internal.Types
18+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
19+-- License : This Source Code Form is subject to the terms of
20+-- the Mozilla Public License, v. 2.0.
21+-- A copy of the MPL can be found in the LICENSE file or
22+-- you can obtain it at http://mozilla.org/MPL/2.0/.
23+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
24+-- Stability : experimental
25+-- Portability : non-portable (GHC extensions)
26+
27+module Network.PagerDuty.Internal.Types where
28+
29+import Control.Applicative
30+import Control.Lens hiding ((.=))
31+import Control.Monad.IO.Class
32+import Data.Aeson hiding (Error)
33+import Data.Aeson.Types (Parser)
34+import Data.ByteString (ByteString)
35+import qualified Data.ByteString.Char8 as BS
36+import Data.ByteString.Conversion hiding (List)
37+import Data.Default.Class
38+import Data.Function (on)
39+import qualified Data.HashMap.Strict as Map
40+import Data.List (deleteBy, intersperse)
41+import Data.Monoid
42+import Data.String
43+import Data.Text (Text)
44+import qualified Data.Text as Text
45+import qualified Data.Text.Encoding as Text
46+import Data.Time
47+import GHC.TypeLits
48+import Network.HTTP.Client (Manager)
49+import Network.HTTP.Types
50+import Network.HTTP.Types.QueryLike
51+import Network.PagerDuty.Internal.Query
52+import Network.PagerDuty.Internal.TH
53+import System.Locale
54+
55+-- FIXME: Verify IncidentId/IncidentKey .. *Id/*Key are actually different or needed
56+
57+newtype CSV a = CSV [a]
58+ deriving (Eq, Show, Monoid)
59+
60+makePrisms ''CSV
61+
62+instance ToByteString a => QueryValues (CSV a)
63+
64+instance ToByteString a => ToByteString (CSV a) where
65+ builder (CSV xs) = mconcat . intersperse "," $ map builder xs
66+
67+instance FromJSON a => FromJSON (CSV a) where
68+ parseJSON = withText "comma_separated_value" $
69+ fmap CSV . traverse (parseJSON . String) . Text.split (== ',')
70+
71+instance ToByteString a => ToJSON (CSV a) where
72+ toJSON = String . Text.decodeUtf8 . toByteString'
73+
74+newtype List a = L [a]
75+ deriving (Eq, Show, Monoid)
76+
77+deriveJSON ''List
78+makePrisms ''List
79+
80+instance QueryValues a => QueryValues (List a) where
81+ queryValues (L xs) = concatMap queryValues xs
82+
83+newtype Bool' = B Bool
84+ deriving (Eq, Show)
85+
86+deriveJSON ''Bool'
87+makePrisms ''Bool'
88+
89+instance ToByteString Bool' where
90+ builder (B True) = "true"
91+ builder (B False) = "false"
92+
93+instance QueryValues Bool'
94+
95+pattern T = B True
96+pattern F = B False
97+
98+newtype Date = D UTCTime
99+ deriving (Eq, Ord, Show)
100+
101+makePrisms ''Date
102+
103+instance FromJSON Date where
104+ parseJSON = fmap D . parseJSON
105+
106+instance ToJSON Date where
107+ toJSON (D d) = toJSON d
108+
109+instance ToByteString Date where
110+ builder (D d) = builder
111+ (formatTime defaultTimeLocale (iso8601DateFormat $ Just "%XZ") d)
112+
113+instance QueryValues Date
114+
115+newtype TZ = TZ TimeZone
116+ deriving (Eq, Show)
117+
118+makePrisms ''TZ
119+
120+instance FromJSON TZ where
121+ parseJSON = undefined
122+
123+instance ToJSON TZ where
124+ toJSON = toJSON . Text.decodeUtf8 . toByteString'
125+
126+instance ToByteString TZ where
127+ builder (TZ tz) = builder (timeZoneName tz)
128+
129+instance QueryValues TZ
130+
131+instance Default TZ where
132+ def = TZ utc
133+
134+data Security = Basic | Token
135+ deriving (Eq, Show)
136+
137+data Auth (a :: Security) where
138+ AuthBasic :: ByteString -> ByteString -> Auth Basic
139+ AuthToken :: ByteString -> Auth Token
140+
141+deriving instance Eq (Auth a)
142+deriving instance Show (Auth a)
143+
144+newtype SubDomain = SubDomain { subDomain :: ByteString }
145+ deriving (Eq, Show, IsString)
146+
147+domain :: SubDomain -> ByteString
148+domain (SubDomain s)
149+ | base `BS.isSuffixOf` s = s
150+ | otherwise = s <> base
151+ where
152+ base = ".pagerduty.com"
153+
154+-- | The log level and associated logger function.
155+data Logger
156+ = None
157+ | Debug (Text -> IO ())
158+
159+-- | Log a message using the debug logger, or if none is specified noop.
160+debug :: MonadIO m => Logger -> Text -> m ()
161+debug None = const (return ())
162+debug (Debug f) = liftIO . f
163+
164+-- | The environment containing the parameters required to
165+-- make PagerDuty requests.
166+data Env (s :: Security) = Env
167+ { _envDomain :: SubDomain
168+ , _envAuth' :: Auth s
169+ , _envManager :: Manager
170+ , _envLogger :: Logger
171+ }
172+
173+makeLenses ''Env
174+
175+envAuth :: forall s s'. Lens (Env s) (Env s') (Auth s) (Auth s')
176+envAuth = lens _envAuth' (\s x -> s { _envAuth' = x })
177+
178+prod :: SubDomain -> Auth s -> Manager -> Env s
179+prod d a m = Env d a m None
180+
181+newtype Code = Code Integer
182+ deriving (Eq, Show)
183+
184+deriveJSON ''Code
185+
186+description :: Code -> Text
187+description (Code c) =
188+ case c of
189+ 2000 -> "Internal Error"
190+ 2001 -> "Invalid Input Provided"
191+ 2002 -> "Arguments Caused Error"
192+ 2003 -> "Missing Arguments"
193+ 2004 -> "Invalid 'since' or 'until' Parameter Values"
194+ 2005 -> "Invalid Query Date Range"
195+ 2006 -> "Authentication Failed"
196+ 2007 -> "Account Not Found"
197+ 2008 -> "Account Locked"
198+ 2009 -> "Only HTTPS Allowed For This Call"
199+ 2010 -> "Access Denied"
200+ 2011 -> "The action requires a 'requester_id' to be specified"
201+ 2012 -> "Your account is expired and cannot use the API"
202+ _ -> "Unrecognised error code"
203+
204+class HasMessage s a | s -> a where
205+ -- | A short human-readable message describing the error.
206+ message :: Lens' s a
207+
208+class HasErrors s a | s -> a where
209+ -- | A list of human-readable reasons for the error. These values,
210+ -- and even their format, are subject to change.
211+ errors :: Lens' s a
212+
213+data IntegrationError = IntegrationError
214+ { _ieStatus :: Text
215+ , _ieMessage :: Text
216+ , _ieErrors :: [Text]
217+ } deriving (Eq, Show)
218+
219+deriveRecord ''IntegrationError
220+
221+instance HasMessage IntegrationError Text where message = ieMessage
222+instance HasErrors IntegrationError [Text] where errors = ieErrors
223+
224+status :: Lens' IntegrationError Text
225+status = ieStatus
226+
227+data RESTError = RESTError
228+ { _reCode :: Code
229+ , _reMessage :: Text
230+ , _reErrors :: [Text]
231+ } deriving (Eq, Show)
232+
233+deriveRecord ''RESTError
234+
235+instance HasMessage RESTError Text where message = reMessage
236+instance HasErrors RESTError [Text] where errors = reErrors
237+
238+-- | In the case of an error, the PagerDuty error code can give further details
239+-- on the nature of the error.
240+--
241+-- /See:/ 'description'
242+code :: Lens' RESTError Code
243+code = reCode
244+
245+data Error
246+ = Internal Text
247+ | Integration IntegrationError
248+ | REST RESTError
249+ deriving (Eq, Show)
250+
251+instance FromJSON Error where
252+ parseJSON o = (REST <$> parseJSON o)
253+ <|> (Integration <$> parseJSON o)
254+
255+makePrisms ''Error
256+
257+instance HasMessage Error Text where
258+ message = lens f g
259+ where
260+ f (Internal x) = x
261+ f (Integration s) = _ieMessage s
262+ f (REST s) = _reMessage s
263+
264+ g (Internal _) x = Internal x
265+ g (Integration s) x = Integration $ s { _ieMessage = x }
266+ g (REST s) x = REST $ s { _reMessage = x }
267+
268+data Pager = Pager
269+ { _pgOffset :: !Int
270+ -- ^ The offset used in the execution of the query.
271+ , _pgLimit :: !Int
272+ -- ^ The limit used in the execution of the query.
273+ , _pgTotal :: !Int
274+ -- ^ The total number of records available.
275+ , _pgQuery :: Maybe Text
276+ -- ^ An optional query= value to be added to the query string.
277+ } deriving (Eq, Show)
278+
279+makeLenses ''Pager
280+
281+instance FromJSON a => FromJSON (a, Maybe Pager) where
282+ parseJSON = withObject "paginated" $ \o -> (,)
283+ <$> parseJSON (Object o)
284+ <*> optional (parse o)
285+ where
286+ parse o = Pager
287+ -- The offset of the first record returned.
288+ -- Default is 0.
289+ <$> o .: "offset" .!= 0
290+ -- The number of records returned.
291+ -- Default (and max limit) is 100 for most APIs.
292+ <*> o .: "limit" .!= 100
293+ <*> o .: "total"
294+ <*> o .:? "query"
295+
296+instance ToJSON Pager where
297+ toJSON p = object
298+ [ "offset" .= _pgOffset p
299+ , "limit" .= _pgLimit p
300+ ]
301+
302+-- | A path segment.
303+data Path where
304+ Path :: Path
305+ Seg :: ToByteString a => a -> Path
306+
307+instance Monoid Path where
308+ mempty = Path
309+ mappend x Path = x
310+ mappend Path y = y
311+ mappend (Seg x) (Seg y) = Seg (builder x <> "/" <> builder y)
312+
313+instance IsString Path where
314+ fromString = Seg
315+
316+instance ToByteString Path where
317+ builder Path = mempty
318+ builder (Seg x) = builder x
319+
320+(%) :: ToByteString a => Path -> a -> Path
321+a % b = a <> Seg b
322+
323+renderPath :: Path -> ByteString
324+renderPath = toByteString' . mappend v1
325+ where
326+ v1 :: Path
327+ v1 = "/api/v1"
328+
329+data Request a (s :: Security) b where
330+ Request :: (QueryLike a, ToJSON a)
331+ => { _rqMeth :: !StdMethod
332+ , _rqPath :: Path
333+ , _rqQuery :: Query
334+ , _rqBody :: a
335+ , _rqPager :: Maybe Pager
336+ , _rqUnwrap :: Value -> Parser Value
337+ }
338+ -> Request a s b
339+
340+instance ToJSON (Request a s b) where
341+ -- Manually unwrapped to ensure GADT constraint holds.
342+ toJSON (Request _ _ _ b p _) = Object $
343+ let Object x = toJSON b
344+ in case toJSON p of
345+ (Object y) -> x <> y
346+ _ -> x
347+
348+type Unwrap = Getting (First Value) Value Value
349+
350+-- | Create a defaulted request from the payload type.
351+mk :: (QueryLike a, ToJSON a) => a -> Request a s b
352+mk x = Request GET mempty mempty x Nothing pure
353+
354+empty :: Request Empty s r
355+empty = mk Empty
356+
357+-- | Lens into the body of a request.
358+upd :: (QueryLike a, ToJSON a) => Lens' (Request a s b) a
359+upd = lens _rqBody (\(Request m p q _ g u) x -> Request m p q x g u)
360+
361+-- | Drop the security constraint.
362+auth :: Request a s b -> Request a t b
363+auth (Request x m p q g u) = Request x m p q g u
364+
365+meth :: Lens' (Request a s b) StdMethod
366+meth = lens _rqMeth (\r x -> r { _rqMeth = x })
367+
368+path :: Lens' (Request a s b) Path
369+path = lens _rqPath (\r x -> r { _rqPath = x })
370+
371+query :: QueryValueLike v
372+ => Lens (Request a s b) (Request a s b) Query [(ByteString, v)]
373+query = lens _rqQuery (\r x -> r { _rqQuery = toQuery x })
374+
375+pager :: Lens' (Request a s b) (Maybe Pager)
376+pager = lens _rqPager (\r x -> r { _rqPager = x })
377+
378+unwrap :: Setter (Request a s b) (Request a s b) (Value -> Parser Value) Unwrap
379+unwrap f r = f (_rqUnwrap r) <&> \k -> r { _rqUnwrap = g k }
380+ where
381+ g k x = maybe (fail "Failed to extract nested keys.") return (x ^? k)
382+
383+-- | Exists primarily to obtain a constraint for the 'paginate' function.
384+class Paginate a where
385+ next :: Request a s b -> Maybe Pager -> Maybe (Request a s b)
386+ next rq = maybe Nothing go
387+ where
388+ go x | x^.pgTotal == 0 = Nothing
389+ | otherwise = Just $
390+ rq & pager ?~ (x & pgOffset +~ x^.pgTotal)
391+ & query %~ (add . clear)
392+ where
393+ add :: Query -> Query
394+ add = maybe id ((:) . (k,) . Just . Text.encodeUtf8) (x^.pgQuery)
395+
396+ clear :: Query -> Query
397+ clear = deleteBy ((==) `on` fst) (k, Nothing)
398+
399+ k :: ByteString
400+ k = "query"
401+
402+newtype Key (a :: Symbol) = Key Text
403+ deriving (Eq, Show, IsString)
404+
405+instance FromJSON (Key a) where
406+ parseJSON = withText "key" (return . Key)
407+
408+instance ToJSON (Key a) where
409+ toJSON (Key k) = toJSON k
410+
411+instance ToByteString (Key a) where
412+ builder (Key k) = builder k
413+
414+instance QueryValues (Key a)
415+
416+instance QueryValueLike (Key a) where
417+ toQueryValue = Just . toByteString'
418+
419+type ServiceKey = Key "service"
420+type IncidentKey = Key "incident"
421+
422+newtype Id (a :: Symbol) = Id Text
423+ deriving (Eq, Show, IsString)
424+
425+instance FromJSON (Id a) where
426+ parseJSON = withText "id" (return . Id)
427+
428+instance ToJSON (Id a) where
429+ toJSON (Id i) = toJSON i
430+
431+instance ToByteString (Id a) where
432+ builder (Id i) = builder i
433+
434+instance QueryValues (Id a)
435+
436+instance QueryValueLike (Id a) where
437+ toQueryValue = Just . toByteString'
438+
439+type AlertId = Id "alert"
440+type ContactId = Id "contact"
441+type EmailFilterId = Id "email-filter"
442+type EscalationPolicyId = Id "escalation-policy"
443+type EscalationRuleId = Id "escalation-rule"
444+type IncidentId = Id "incident"
445+type LogEntryId = Id "log-entry"
446+type NoteId = Id "note"
447+type NotificationRuleId = Id "notification-rule"
448+type OverrideId = Id "schedule-override"
449+type RequesterId = Id "requester"
450+type ScheduleId = Id "schedule"
451+type ServiceId = Id "service"
452+type UserId = Id "user"
453+type VendorId = Id "vendor"
454+type WebhookId = Id "webhook"
455+type WindowId = Id "maintenance-window"
456+
457+data Empty = Empty
458+
459+instance ToJSON Empty where
460+ toJSON = const (object [])
461+
462+instance FromJSON Empty where
463+ parseJSON = withObject "empty" f
464+ where
465+ f !o | Map.null o = pure Empty
466+ | otherwise = fail "Unexpected non-empty JSON object."
467+
468+instance QueryLike Empty where
469+ toQuery = const []
470+
471+newtype Address = Address Text
472+ deriving (Eq, Show)
473+
474+deriveJSON ''Address
475+makePrisms ''Address
476+
477+instance ToByteString Address where
478+ builder (Address a) = builder a
479+
480+instance QueryValues Address
src/Network/PagerDuty/REST.hsnew file+104
@@ -0,0 +1,104 @@
1+{-# LANGUAGE GADTs #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+-- Module : Network.PagerDuty.REST
5+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
6+-- License : This Source Code Form is subject to the terms of
7+-- the Mozilla Public License, v. 2.0.
8+-- A copy of the MPL can be found in the LICENSE file or
9+-- you can obtain it at http://mozilla.org/MPL/2.0/.
10+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
11+-- Stability : experimental
12+-- Portability : non-portable (GHC extensions)
13+
14+module Network.PagerDuty.REST
15+ (
16+
17+ -- * Sending requests
18+ send
19+ , paginate
20+
21+ -- * Configuration
22+ -- ** Environment
23+ , Env
24+ , envDomain
25+ , envAuth
26+ , envManager
27+ , envLogger
28+
29+ -- ** Requests
30+ , sendWith
31+ , paginateWith
32+ ) where
33+
34+import Control.Applicative
35+import Control.Lens
36+import Control.Monad
37+import Control.Monad.IO.Class
38+import Control.Monad.Trans
39+import Data.Aeson (FromJSON)
40+import Data.Conduit
41+import Data.Default.Class
42+import Data.Monoid
43+import Network.HTTP.Client (Manager)
44+import qualified Network.HTTP.Client as Client
45+import Network.HTTP.Types
46+import Network.PagerDuty.Internal.IO
47+import Network.PagerDuty.Internal.Types
48+
49+-- FIXME: verify correct actions are all paginated
50+-- FIXME: Ensure RequesterId parameter is always most significant param
51+-- FIXME: add smart constructors for all types, for testing purposes
52+
53+send :: (MonadIO m, FromJSON b)
54+ => SubDomain
55+ -> Auth s
56+ -> Manager
57+ -> Request a s b
58+ -> m (Either Error b)
59+send d a m = sendWith (prod d a m)
60+
61+-- | /See:/ 'sendWith'
62+sendWith :: (MonadIO m, FromJSON b)
63+ => Env s
64+ -> Request a s b
65+ -> m (Either Error b)
66+sendWith e = liftM (fmap fst) . http e
67+
68+paginate :: (MonadIO m, Paginate a, FromJSON b)
69+ => SubDomain
70+ -> Auth s
71+ -> Manager
72+ -> Request a s b
73+ -> Source m (Either Error b)
74+paginate d a m = paginateWith (prod d a m)
75+
76+-- | /See:/ 'paginateWith'
77+paginateWith :: (MonadIO m, Paginate a, FromJSON b)
78+ => Env s
79+ -> Request a s b
80+ -> Source m (Either Error b)
81+paginateWith e = go
82+ where
83+ go rq = do
84+ rs <- lift (http e rq)
85+ yield (fst <$> rs)
86+ either (const (return ()))
87+ (maybe (return ()) go . next rq . snd)
88+ rs
89+
90+http :: (MonadIO m, FromJSON b)
91+ => Env s
92+ -> Request a s b
93+ -> m (Either Error (b, Maybe Pager))
94+http e rq = request (e ^. envManager) (e ^. envLogger) rq $ raw
95+ { Client.host = domain (e ^. envDomain)
96+ , Client.path = renderPath (rq ^. path)
97+ , Client.queryString = renderQuery False (rq ^. query)
98+ }
99+ where
100+ raw = case e ^. envAuth of
101+ AuthBasic u p -> Client.applyBasicAuth u p def
102+ AuthToken t -> def
103+ { Client.requestHeaders = [("Authorization", "Token token=" <> t)]
104+ }
src/Network/PagerDuty/REST/Alerts.hsnew file+123
@@ -0,0 +1,123 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+{-# LANGUAGE TypeFamilies #-}
5+
6+-- Module : Network.PagerDuty.REST.Alerts
7+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
8+-- License : This Source Code Form is subject to the terms of
9+-- the Mozilla Public License, v. 2.0.
10+-- A copy of the MPL can be found in the LICENSE file or
11+-- you can obtain it at http://mozilla.org/MPL/2.0/.
12+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
13+-- Stability : experimental
14+-- Portability : non-portable (GHC extensions)
15+
16+-- | When an incident is triggered or when it is escalated it creates an alert
17+-- (also known as a notification). Alerts are messages containing the details
18+-- of the incident, and can be sent through SMS, email, phone calls,
19+-- and push notifications.
20+--
21+-- This API allows you to access read-only data regarding what alerts have been
22+-- sent to your users.
23+--
24+-- /See:/ <http://developer.pagerduty.com/documentation/rest/alerts>
25+module Network.PagerDuty.REST.Alerts
26+ (
27+ -- * List Alerts
28+ ListAlerts
29+ , listAlerts
30+ , laSince
31+ , laUntil
32+ , laFilter
33+ , laTimeZone
34+
35+ -- * Types
36+ , AlertType (..)
37+
38+ , Alert
39+ , alertId
40+ , alertType
41+ , alertStartedAt
42+ , alertUser
43+ , alertAddress
44+ ) where
45+
46+import Control.Lens
47+import Data.Aeson.Lens
48+import Data.Time
49+import Network.PagerDuty.REST.Users (User)
50+import Network.PagerDuty.Internal.TH
51+import Network.PagerDuty.Internal.Types
52+
53+alerts :: Path
54+alerts = "alerts"
55+
56+data AlertType
57+ = SMS
58+ | Email
59+ | Phone
60+ | Push
61+ deriving (Eq, Show)
62+
63+deriveNullary ''AlertType
64+
65+data Alert = Alert
66+ { _alertId :: AlertId
67+ , _alertType :: AlertType
68+ , _alertStartedAt :: Date
69+ , _alertUser :: User
70+ , _alertAddress :: Address
71+ } deriving (Eq, Show)
72+
73+deriveRecord ''Alert
74+
75+data ListAlerts = ListAlerts
76+ { _laSince' :: Date
77+ , _laUntil' :: Date
78+ , _laFilter' :: Maybe AlertType
79+ , _laTimeZone' :: Maybe TZ
80+ } deriving (Eq, Show)
81+
82+instance Paginate ListAlerts
83+
84+queryRequest ''ListAlerts
85+
86+-- | The start of the date range over which you want to search.
87+laSince :: Lens' (Request ListAlerts s b) UTCTime
88+laSince = upd.laSince'._D
89+
90+-- | The end of the date range over which you want to search.
91+-- This should be in the same format as 'since'.
92+--
93+-- The size of the date range must be less than 3 months.
94+laUntil :: Lens' (Request ListAlerts s b) UTCTime
95+laUntil = upd.laUntil'._D
96+
97+-- | Returns only the alerts of the said 'AlertType' type.
98+laFilter :: Lens' (Request ListAlerts s b) (Maybe AlertType)
99+laFilter = upd.laFilter'
100+
101+-- | Time zone in which dates in the result will be rendered.
102+--
103+-- /Default:/ account time zone.
104+laTimeZone :: Lens' (Request ListAlerts s b) (Maybe TimeZone)
105+laTimeZone = upd.laTimeZone'.mapping _TZ
106+
107+-- | List existing alerts for a given time range, optionally filtered by type
108+-- (SMS, Email, Phone, or Push).
109+--
110+-- @GET \/alerts@
111+--
112+-- /See:/ <http://developer.pagerduty.com/documentation/rest/alerts/list>
113+listAlerts :: Date -- ^ 'laSince'
114+ -> Date -- ^ 'laUntil'
115+ -> Request ListAlerts s [Alert]
116+listAlerts s u =
117+ mk ListAlerts
118+ { _laSince' = s
119+ , _laUntil' = u
120+ , _laFilter' = Nothing
121+ , _laTimeZone' = Nothing
122+ } & path .~ alerts
123+ & unwrap .~ key "alerts"
src/Network/PagerDuty/REST/EscalationPolicies.hsnew file+244
@@ -0,0 +1,244 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.EscalationPolicies
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | This API lets you access and manipulate escalation policies and rules.
20+--
21+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies>
22+module Network.PagerDuty.REST.EscalationPolicies
23+ (
24+ -- * List Policies
25+ ListPolicies
26+ , listPolicies
27+ , lpQuery
28+
29+ -- * List On Call Policies
30+ , listOnCallPolicies
31+
32+ -- * Create Policy
33+ , CreatePolicy
34+ , createPolicy
35+ , cpName
36+ , cpRepeatEnabled
37+ , cpNumLoops
38+ , cpEscalationRules
39+
40+ -- * Get Policy
41+ , getPolicy
42+
43+ -- * Update Policy
44+ , UpdatePolicy
45+ , updatePolicy
46+ , upName
47+ , upRepeatEnabled
48+ , upNumLoops
49+ , upEscalationRules
50+
51+ -- * Delete Policy
52+ , deletePolicy
53+
54+ -- * Types
55+ , Policy
56+ , pId
57+ , pName
58+ , pNumLoops
59+ , pRules
60+ , pServices
61+ ) where
62+
63+import Control.Lens
64+import Data.Aeson.Lens
65+import Data.Text (Text)
66+import Network.HTTP.Types
67+import Network.PagerDuty.REST.Services (Service)
68+import Network.PagerDuty.Internal.TH
69+import Network.PagerDuty.Internal.Types
70+
71+import Network.PagerDuty.REST.EscalationPolicies.EscalationRules
72+
73+default (Path)
74+
75+policies :: Path
76+policies = "escalation_policies"
77+
78+data Policy = Policy
79+ { _pId :: EscalationPolicyId
80+ , _pName :: Text
81+ , _pNumLoops :: !Int
82+ , _pRules :: [Rule]
83+ , _pServices :: [Service]
84+ } deriving (Eq, Show)
85+
86+deriveJSON ''Policy
87+
88+-- | The ID of the escalation policy.
89+makeLens "_pId" ''Policy
90+
91+-- | The name of the escalation policy.
92+makeLens "_pName" ''Policy
93+
94+-- | The number of times the escalation policy will repeat after
95+-- reaching the end of its escalation.
96+makeLens "_pNumLoops" ''Policy
97+
98+-- | A list of the policy's escalation rules in order of escalation.
99+makeLens "_pRules" ''Policy
100+
101+-- | A list of services using this escalation policy.
102+makeLens "_pServices" ''Policy
103+
104+newtype ListPolicies = ListPolicies
105+ { _lpQuery' :: Maybe Text
106+ } deriving (Eq, Show)
107+
108+instance Paginate ListPolicies
109+
110+queryRequest ''ListPolicies
111+
112+-- | Filters the result, showing only the escalation policies
113+-- whose names match the query.
114+lpQuery :: Lens' (Request ListPolicies s b) (Maybe Text)
115+lpQuery = upd.lpQuery'
116+
117+-- | List all the existing escalation policies.
118+--
119+-- @GET \/escalation_policies@
120+--
121+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/list>
122+listPolicies :: Request ListPolicies s [Policy]
123+listPolicies =
124+ mk ListPolicies
125+ { _lpQuery' = Nothing
126+ } & path .~ policies
127+ & unwrap .~ key "escalation_policies"
128+
129+-- | List all the existing escalation policies with currently on-call users.
130+--
131+-- If the start and end of an on-call object are null, then the user is always
132+-- on-call for an escalation policy level.
133+--
134+-- @GET \/escalation_policies\/on_call@
135+--
136+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/on_call>
137+listOnCallPolicies :: Request ListPolicies s [Policy]
138+listOnCallPolicies = listPolicies & path .~ policies % "on_call"
139+
140+data CreatePolicy = CreatePolicy
141+ { _cpName' :: Text
142+ , _cpRepeatEnabled' :: Bool'
143+ , _cpNumLoops' :: Maybe Int
144+ , _cpEscalationRules' :: [Rule] -- ^ Should be List1
145+ } deriving (Eq, Show)
146+
147+jsonRequest ''CreatePolicy
148+
149+-- | The name of the escalation policy.
150+cpName :: Lens' (Request CreatePolicy s b) Text
151+cpName = upd.cpName'
152+
153+-- | Whether or not to allow this policy to repeat its escalation
154+-- rules after the last rule is finished.
155+--
156+-- /Default:/ false.
157+cpRepeatEnabled :: Lens' (Request CreatePolicy s b) Bool
158+cpRepeatEnabled = upd.cpRepeatEnabled'._B
159+
160+-- | The number of times to loop over the set of rules in this escalation policy.
161+cpNumLoops :: Lens' (Request CreatePolicy s b) (Maybe Int)
162+cpNumLoops = upd.cpNumLoops'
163+
164+-- | The escalation rules for this policy.
165+cpEscalationRules :: Lens' (Request CreatePolicy s b) [Rule]
166+cpEscalationRules = upd.cpEscalationRules'
167+
168+-- | Creates an existing escalation policy and rules.
169+--
170+-- @POST \/escalation_policies@
171+--
172+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/create>
173+createPolicy :: EscalationPolicyId
174+ -> Text -- ^ 'creName'
175+ -> [Rule] -- ^ 'creEscalationRules'
176+ -> Request CreatePolicy s Policy
177+createPolicy i n rs =
178+ mk CreatePolicy
179+ { _cpName' = n
180+ , _cpRepeatEnabled' = F
181+ , _cpNumLoops' = Nothing
182+ , _cpEscalationRules' = rs
183+ } & meth .~ PUT
184+ & path .~ policies % i
185+ & unwrap .~ key "escalation_policy"
186+
187+-- | Get information about an existing escalation policy and its rules.
188+--
189+-- @GET \/escalation_policies\/\:id@
190+--
191+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/show>
192+getPolicy :: EscalationPolicyId -> Request Empty s Policy
193+getPolicy i = empty & path .~ policies % i & unwrap .~ key "escalation_policy"
194+
195+data UpdatePolicy = UpdatePolicy
196+ { _upName' :: Maybe Text
197+ , _upRepeatEnabled' :: Maybe Bool'
198+ , _upNumLoops' :: Maybe Int
199+ , _upEscalationRules' :: [Rule]
200+ } deriving (Eq, Show)
201+
202+jsonRequest ''UpdatePolicy
203+
204+-- | The name of the escalation policy.
205+upName :: Lens' (Request UpdatePolicy s b) (Maybe Text)
206+upName = upd.upName'
207+
208+-- | Whether or not to allow this policy to repeat its escalation
209+-- rules after the last rule is finished.
210+upRepeatEnabled :: Lens' (Request UpdatePolicy s b) (Maybe Bool)
211+upRepeatEnabled = upd.upRepeatEnabled'.mapping _B
212+
213+-- | The number of times to loop over the set of rules in this escalation policy.
214+upNumLoops :: Lens' (Request UpdatePolicy s b) (Maybe Int)
215+upNumLoops = upd.upNumLoops'
216+
217+-- | The escalation rules for this policy.
218+upEscalationRules :: Lens' (Request UpdatePolicy s b) [Rule]
219+upEscalationRules = upd.upEscalationRules'
220+
221+-- | Updates an existing escalation policy and rules.
222+--
223+-- @PUT \/escalation_policies\/\:id@
224+--
225+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/update>
226+updatePolicy :: EscalationPolicyId -> Request UpdatePolicy s Policy
227+updatePolicy i =
228+ mk UpdatePolicy
229+ { _upName' = Nothing
230+ , _upRepeatEnabled' = Nothing
231+ , _upNumLoops' = Nothing
232+ , _upEscalationRules' = []
233+ } & meth .~ PUT
234+ & path .~ policies % i
235+ & unwrap .~ key "escalation_policy"
236+
237+-- | Deletes an existing escalation policy and rules. The escalation policy
238+-- must not be in use by any services.
239+--
240+-- @DELETE \/escalation_policies\/\:id@
241+--
242+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/delete>
243+deletePolicy :: EscalationPolicyId -> Request Empty s Empty
244+deletePolicy i = empty & meth .~ DELETE & path .~ policies % i
src/Network/PagerDuty/REST/EscalationPolicies/EscalationRules.hsnew file+277
@@ -0,0 +1,277 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+
6+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
7+
8+-- Module : Network.PagerDuty.REST.EscalationPolicies.EscalationRules
9+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
10+-- License : This Source Code Form is subject to the terms of
11+-- the Mozilla Public License, v. 2.0.
12+-- A copy of the MPL can be found in the LICENSE file or
13+-- you can obtain it at http://mozilla.org/MPL/2.0/.
14+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
15+-- Stability : experimental
16+-- Portability : non-portable (GHC extensions)
17+
18+-- | This API lets you access and manipulate escalation rules of an existing
19+-- escalation policy.
20+--
21+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules>
22+module Network.PagerDuty.REST.EscalationPolicies.EscalationRules
23+ (
24+ -- * List Rules
25+ listRules
26+
27+ -- * Create Rule
28+ , CreateRule
29+ , createRule
30+ , crEscalationDelayInMinutes
31+ , crTargets
32+
33+ -- * Get Rule
34+ , getRule
35+
36+ -- * Update Rules
37+ , UpdateRules
38+ , updateRules
39+ , urEscalationRules
40+
41+ -- * Update Rule
42+ , UpdateRule
43+ , updateRule
44+ , urEscalationDelayInMinutes
45+ , urTargets
46+
47+ -- * Delete Rule
48+ , deleteRule
49+
50+ -- * Types
51+ , HasUserInfo (..)
52+
53+ , Target (..)
54+ , _TSchedule
55+ , _TUser
56+
57+ , ScheduleTarget
58+ , stId
59+ , stName
60+
61+ , Rule
62+ , rId
63+ , rEscalationDelayInMinutes
64+ , rTargets
65+ ) where
66+
67+import Control.Applicative ((<$>))
68+import Control.Lens hiding ((.=))
69+import Data.Aeson
70+import qualified Data.HashMap.Strict as Map
71+import Data.Text (Text)
72+import Network.HTTP.Types
73+import Network.PagerDuty.REST.Users
74+import Network.PagerDuty.Internal.TH
75+import Network.PagerDuty.Internal.Types
76+
77+default (Path)
78+
79+-- FIXME: The lenses here need to operate over (Request x s b) using upd
80+
81+rules :: EscalationPolicyId -> Path
82+rules p = "escalation_policies" % p % "escalation_rules"
83+
84+data TargetType
85+ = TargetSchedule
86+ | TargetUser
87+
88+deriveJSONWith (dropped 6 underscored) ''TargetType
89+
90+data TargetId
91+ = ScheduleId ScheduleId
92+ | UserId UserId
93+ deriving (Eq, Show)
94+
95+instance FromJSON TargetId where
96+ parseJSON = withObject "target" $ \o -> do
97+ t <- o .: "type"
98+ case t of
99+ TargetSchedule -> ScheduleId <$> o .: "id"
100+ TargetUser -> UserId <$> o .: "id"
101+
102+instance ToJSON TargetId where
103+ toJSON (ScheduleId s) = object ["type" .= TargetSchedule, "id" .= s]
104+ toJSON (UserId u) = object ["type" .= TargetUser, "id" .= u]
105+
106+data ScheduleTarget = ScheduleTarget
107+ { _stId :: ScheduleId
108+ , _stName :: Text
109+ } deriving (Eq, Show)
110+
111+deriveJSON ''ScheduleTarget
112+
113+-- | The id of the target.
114+makeLens "_stId" ''ScheduleTarget
115+
116+-- | The name of the target.
117+makeLens "_stName" ''ScheduleTarget
118+
119+data Target
120+ = TSchedule ScheduleTarget
121+ | TUser UserInfo
122+ deriving (Eq, Show)
123+
124+makePrisms ''Target
125+
126+instance FromJSON Target where
127+ parseJSON = withObject "target" $ \o -> do
128+ t <- o .: "type"
129+ case t of
130+ TargetSchedule -> TSchedule <$> parseJSON (Object o)
131+ TargetUser -> TUser <$> parseJSON (Object o)
132+
133+instance ToJSON Target where
134+ toJSON t = Object (Map.insert "type" (String k) o)
135+ where
136+ (k, Object o) = case t of
137+ TSchedule s -> ("schedule", toJSON s)
138+ TUser u -> ("user", toJSON u)
139+
140+data Rule = Rule
141+ { _rId :: EscalationRuleId
142+ , _rEscalationDelayInMinutes :: !Int
143+ , _rTargets :: [Target]
144+ } deriving (Eq, Show)
145+
146+deriveJSON ''Rule
147+
148+-- | The ID of the escalation rule.
149+makeLens "_rId" ''Rule
150+
151+-- | The amount of time before an incident escalates away from this rule.
152+makeLens "_rEscalationDelayInMinutes" ''Rule
153+
154+-- | A list of targets which an incident will be assigned to upon reaching this rule.
155+makeLens "_rTargets" ''Rule
156+
157+-- | List all the escalation rules for an existing escalation policy.
158+--
159+-- @GET \/escalation_policies\/\:escalation_policy_id\/escalation_rules@
160+--
161+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/list>
162+listRules :: EscalationPolicyId -> Request Empty s [Rule]
163+listRules p = empty & path .~ rules p
164+
165+-- | Show the escalation rule for an existing escalation policy.
166+--
167+-- @GET \/escalation_policies\/:escalation_policy_id\/escalation_rules\/\:id@
168+--
169+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/show>
170+getRule :: EscalationPolicyId -> EscalationRuleId -> Request Empty s Rule
171+getRule p r = empty & path .~ rules p % r
172+
173+data CreateRule = CreateRule
174+ { _crEscalationDelayInMinutes :: !Int
175+ , _crTargets :: [TargetId] -- ^ list1?
176+ } deriving (Eq, Show)
177+
178+deriveJSON ''CreateRule
179+
180+instance QueryLike CreateRule where
181+ toQuery = const []
182+
183+-- | The escalation timeout in minutes. Must be at least 5 if the rule has
184+-- multiple targets, and at least 1 if not. If an incident is not acknowledged
185+-- within this timeout then it will escalate onto the next escalation rule.
186+makeLens "_crEscalationDelayInMinutes" ''CreateRule
187+
188+-- | The list of the targets an incident should be assigned to upon reaching
189+-- this rule.
190+makeLens "_crTargets" ''CreateRule
191+
192+-- | Creates a new escalation rule for an escalation policy and appends it to
193+-- the end of the existing escalation rules.
194+--
195+-- @POST \/escalation_policies\/\:escalation_policy_id\/escalation_rules@
196+--
197+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/create>
198+createRule :: EscalationPolicyId
199+ -> Int -- ^ 'crEscalationDelayInMinutes'
200+ -> [TargetId] -- ^ 'crTargets'
201+ -> Request CreateRule s Rule
202+createRule p n ts =
203+ mk CreateRule
204+ { _crEscalationDelayInMinutes = n
205+ , _crTargets = ts
206+ } & meth .~ POST
207+ & path .~ rules p
208+
209+newtype UpdateRules = UpdateRules
210+ { _urEscalationRules :: [TargetId] -- ^ list1
211+ } deriving (Eq, Show)
212+
213+deriveJSON ''UpdateRules
214+
215+instance QueryLike UpdateRules where
216+ toQuery = const []
217+
218+-- | An ordered array of escalation rules.
219+makeLens "_urEscalationRules" ''UpdateRules
220+
221+-- | Updates the entire collection of escalation rules for an existing escalation
222+-- policy. The ordering is determined by the positions of each rule in the
223+-- list. To create a new rule, add it to the list without an id. To remove a
224+-- rule, do not include it in the list.
225+--
226+-- @PUT \/escalation_policies\/\:escalation_policy_id\/escalation_rules@
227+--
228+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/multi_update>
229+updateRules :: EscalationPolicyId
230+ -> [TargetId] -- ^ 'urEscalationRules'
231+ -> Request UpdateRules s [Rule]
232+updateRules p rs =
233+ mk UpdateRules
234+ { _urEscalationRules = rs
235+ } & meth .~ PUT
236+ & path .~ rules p
237+
238+data UpdateRule = UpdateRule
239+ { _urEscalationDelayInMinutes :: Maybe Int
240+ , _urTargets :: [TargetId]
241+ } deriving (Eq, Show)
242+
243+deriveJSON ''UpdateRule
244+
245+instance QueryLike UpdateRule where
246+ toQuery = const []
247+
248+-- | The escalation timeout in minutes. Must be at least 5 if the rule has
249+-- multiple targets, and at least 1 if not. If an incident is not acknowledged
250+-- within this timeout then it will escalate onto the next escalation rule.
251+makeLens "_urEscalationDelayInMinutes" ''UpdateRule
252+
253+-- | The list of the targets an incident should be assigned to upon reaching
254+-- this rule.
255+makeLens "_urTargets" ''UpdateRule
256+
257+-- | Updates an escalation rule.
258+--
259+-- @PUT \/escalation_policies\/\:escalation_policy_id\/escalation_rules\/\:id@
260+--
261+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/update>
262+updateRule :: EscalationPolicyId -> EscalationRuleId -> Request UpdateRule s Rule
263+updateRule p r =
264+ mk UpdateRule
265+ { _urEscalationDelayInMinutes = Nothing
266+ , _urTargets = []
267+ } & meth .~ PUT
268+ & path .~ rules p % r
269+
270+-- | Deletes an existing escalation rule that belongs to an escalation
271+-- policy. There must be a matching rule.
272+--
273+-- @DELETE \/escalation_policies\/\:escalation_policy_id\/escalation_rules\/\:id@
274+--
275+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/escalation_rules/delete>
276+deleteRule :: EscalationPolicyId -> EscalationRuleId -> Request Empty s Empty
277+deleteRule p r = empty & meth .~ DELETE & path .~ rules p % r
src/Network/PagerDuty/REST/Incidents.hsnew file+650
@@ -0,0 +1,650 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
11+
12+-- Module : Network.PagerDuty.REST.Incidents
13+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
14+-- License : This Source Code Form is subject to the terms of
15+-- the Mozilla Public License, v. 2.0.
16+-- A copy of the MPL can be found in the LICENSE file or
17+-- you can obtain it at http://mozilla.org/MPL/2.0/.
18+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
19+-- Stability : experimental
20+-- Portability : non-portable (GHC extensions)
21+
22+-- | PagerDuty receives updates (called events) from monitoring systems through
23+-- services (like Nagios, email or generic API calls). Upon receiving an event,
24+-- active services will create a new incident and begin escalating it as
25+-- defined by the service's escalation policy. Events can be de-duplicated into
26+-- existing incidents based on service de-duplication rules to prevent you from
27+-- being overwhelmed by event storms.
28+--
29+-- An incident can be open, acknowledged or resolved. Whenever an incident is
30+-- created, it is assigned to a user (according to the escalation process, as
31+-- described by escalation rules and schedules). The assigned user has a chance
32+-- to either acknowledge that he is working on it, or to resolve it.
33+--
34+-- Resolving an incident closes it, whereas acknowledging it halts the
35+-- escalation process. If the incident is not resolved by the service's
36+-- incident ack timeout it continues up the escalation chain.
37+--
38+-- When an incident is triggered or when it is escalated it creates alerts
39+-- (also known as notifications). Alerts are messages containing the details of
40+-- the incident, and can be sent through SMS, email and phone calls.
41+--
42+-- Incidents and Incident Counts from the last 30 days are returned by
43+-- default. To change this default date range, see the documentation for the
44+-- since, until or dateRange parameters.
45+--
46+-- Note: that to create an incident you need to trigger it through the
47+-- "Network.PagerDuty.Integration" API.
48+--
49+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents>
50+module Network.PagerDuty.REST.Incidents
51+ (
52+ -- * List Incidents
53+ ListIncidents
54+ , listIncidents
55+ , lsSince
56+ , lsUntil
57+ , lsDateRange
58+ , lsStatus
59+ , lsIncidentKey
60+ , lsService
61+ , lsAssignedToUser
62+ , lsTimeZone
63+ , lsSortBy
64+
65+ -- * Count Incidents
66+ , CountIncidents
67+ , countIncidents
68+ , cSince
69+ , cUntil
70+ , cDateRange
71+ , cStatus
72+ , cIncidentKey
73+ , cService
74+ , cAssignedToUser
75+
76+ -- * Get Incident
77+ , getIncident
78+
79+ -- * Update Incidents
80+ , UpdateIncidents
81+ , updateIncidents
82+ , updateIncidentsBasic
83+ , uiIncidents
84+
85+ -- * Resolve Incident
86+ , resolveIncident
87+ , resolveIncidentBasic
88+
89+ -- * Acknowledge Incident
90+ , acknowledgeIncident
91+ , acknowledgeIncidentBasic
92+
93+ -- * Reassign Incident
94+ , ReassignIncident
95+ , reassignIncident
96+ , riEscalationPolicy
97+ , riEscalationLevel
98+ , riAssignedToUser
99+
100+ -- * Types
101+ , Sort (..)
102+ , _Desc
103+ , _Asc
104+
105+ , Field (..)
106+
107+ , IncidentStatus (..)
108+ , UpdateStatus (..)
109+
110+ , UpdateIncident
111+ , iiId
112+ , iiStatus
113+ , iiEscalationLevel
114+ , iiEscalationPolicy
115+ , iiAssignedToUser
116+
117+ , UpdatedIncidents
118+ , uiId
119+ , uiStatus
120+ , uiIncidentNumber
121+ , uiUrl
122+ , uiError
123+
124+ , Assignee
125+ , aAt
126+ , aObject
127+
128+ , HasIncident (..)
129+ , Incident
130+ ) where
131+
132+import Control.Applicative hiding (empty)
133+import Control.Lens
134+import Data.Aeson
135+import Data.ByteString.Conversion (ToByteString(..), toByteString')
136+import Data.Default.Class
137+import Data.Maybe
138+import Data.Monoid hiding (All)
139+import Data.Text (Text)
140+import qualified Data.Text.Encoding as Text
141+import Data.Time
142+import Network.HTTP.Types
143+import Network.PagerDuty.Internal.Query
144+import Network.PagerDuty.Internal.TH
145+import Network.PagerDuty.Internal.Types
146+
147+default (Path)
148+
149+incidents :: Path
150+incidents = "incidents"
151+
152+data Sort a
153+ = Desc a
154+ | Asc a
155+ deriving (Eq, Show)
156+
157+makePrisms ''Sort
158+
159+data Field
160+ = IncidentNumber
161+ -- ^ The number of your incident.
162+ | CreatedOn
163+ -- ^ The date/time the incident was triggered.
164+ | ResolvedOn
165+ -- ^ The date/time the incident was resolved.
166+ deriving (Eq, Show)
167+
168+instance QueryValues (Sort Field) where
169+ queryValues s = case s of
170+ Desc f -> ["desc:" <> field f]
171+ Asc f -> ["asc:" <> field f]
172+ where
173+ field IncidentNumber = "incidentNumber"
174+ field CreatedOn = "createdOn"
175+ field ResolvedOn = "resolvedOn"
176+
177+data Range = All
178+ deriving (Eq, Show)
179+
180+instance QueryValues Range where
181+ queryValues All = ["all"]
182+
183+data IncidentStatus
184+ = ITriggered
185+ | IAcknowledged
186+ | IResolved
187+ | IOther Text
188+ deriving (Eq, Show)
189+
190+instance ToByteString IncidentStatus where
191+ builder = \case
192+ ITriggered -> "triggered"
193+ IAcknowledged -> "acknowledged"
194+ IResolved -> "resolved"
195+ IOther t -> builder t
196+
197+instance FromJSON IncidentStatus where
198+ parseJSON = withText "status" $ pure . \case
199+ "triggered" -> ITriggered
200+ "acknowledged" -> IAcknowledged
201+ "resolved" -> IResolved
202+ t -> IOther t
203+
204+instance ToJSON IncidentStatus where
205+ toJSON = String . Text.decodeUtf8 . toByteString'
206+
207+data Assignee = Assignee
208+ { _aAt :: Date
209+ , _aObject :: Object
210+ } deriving (Eq, Show)
211+
212+deriveRecord ''Assignee
213+
214+data Incident = Incident
215+ { _iIncidentNumber' :: !Int
216+ , _iStatus' :: !IncidentStatus
217+ , _iCreatedOn' :: Date
218+ , _iHtmlUrl' :: Text
219+ , _iIncidentKey' :: Maybe IncidentKey
220+ , _iService' :: Object
221+ , _iEscalationPolicy' :: Maybe Object
222+ , _iAssignedTo' :: [Assignee]
223+ , _iAcknowledgers' :: [Assignee]
224+ , _iLastStatusChangeBy' :: Maybe Assignee
225+ , _iLastStatusChangeOn' :: Maybe Date
226+ , _iTriggerSummaryData' :: Object
227+ , _iTriggerDetailsHtmlUrl' :: Text
228+ } deriving (Eq, Show)
229+
230+deriveRecord ''Incident
231+
232+class HasIncident a where
233+ incident :: Lens' a Incident
234+
235+ -- | The number of the incident. This is unique across your account.
236+ iIncidentNumber :: Lens' a Int
237+
238+ -- | The current status of the incident. Valid statuses are:
239+ iStatus :: Lens' a IncidentStatus
240+
241+ -- | The date/time the incident was triggered.
242+ iCreatedOn :: Lens' a UTCTime
243+
244+ -- | The PagerDuty website URL where the incident can be viewed and further
245+ -- actions taken. This is not the resource URL.
246+ iHtmlUrl :: Lens' a Text
247+
248+ -- | The incident's de-duplication key. See the PagerDuty Integration API
249+ -- docs for further details.
250+ iIncidentKey :: Lens' a (Maybe IncidentKey)
251+
252+ -- | The PagerDuty service that the incident belongs to. The service will
253+ -- contain fields of its own.
254+ iService :: Lens' a Object
255+
256+ -- | The escalation policy that the incident belongs to. The policy will
257+ -- contain fields of its own.
258+ iEscalationPolicy :: Lens' a (Maybe Object)
259+
260+ -- | The list of assignments of the incident. An assignment is an object
261+ -- containing the assigned user as well as the date/time the incident was
262+ -- assigned to that user. The user will contain fields of its own.
263+ -- This list is empty if the status of the incident is resolved.
264+ iAssignedTo :: Lens' a [Assignee]
265+
266+ -- | The list of acknowledgements of the incident. An acknowledgement is an
267+ -- object containing the acknowleding object (either a user or the
268+ -- integration API) as well as the date/time the incident was acknowledged.
269+ -- This field is only present if the status of the incident is acknowledged.
270+ -- This field is sorted in ascending order by acknowledgement time.
271+ iAcknowledgers :: Lens' a [Assignee]
272+
273+ -- | The user who is responsible for the incident's last status change. If
274+ -- the incident is in the acknowledged or resolved status, this will be the user
275+ -- that took the first acknowledged or resolved action. If the incident was
276+ -- automatically resolved (say through the Event Integration API), or if the
277+ -- incident is in the triggered state, this will be null. User fields are the
278+ -- same as in the assignedToUser field above.
279+ iLastStatusChangeBy :: Lens' a (Maybe Assignee)
280+
281+ -- | The date/time the incident's status last changed.
282+ iLastStatusChangeOn :: Lens' a (Maybe UTCTime)
283+
284+ -- | Some condensed information regarding the initial event that triggered
285+ -- this incident. This data will be a set of key/value pairs that vary depending
286+ -- on what sort of event triggered the incident (email, Event API request,
287+ -- etc). For instance, if an email triggered the incident, then the
288+ -- triggerSummaryData will likely contain a subject. There are no guarantees
289+ -- o nthe full presence of this data for every incident.
290+ iTriggerSummaryData :: Lens' a Object
291+
292+ -- | The PagerDuty website URL where the full details regarding the initial
293+ -- event that triggered this incident can be found. (This is not the resource
294+ -- URL.)
295+ iTriggerDetailsHtmlUrl :: Lens' a Text
296+
297+ iIncidentNumber = incident.iIncidentNumber'
298+ iStatus = incident.iStatus'
299+ iCreatedOn = incident.iCreatedOn'._D
300+ iHtmlUrl = incident.iHtmlUrl'
301+ iIncidentKey = incident.iIncidentKey'
302+ iService = incident.iService'
303+ iEscalationPolicy = incident.iEscalationPolicy'
304+ iAssignedTo = incident.iAssignedTo'
305+ iAcknowledgers = incident.iAcknowledgers'
306+ iLastStatusChangeBy = incident.iLastStatusChangeBy'
307+ iLastStatusChangeOn = incident.iLastStatusChangeOn'.mapping _D
308+ iTriggerSummaryData = incident.iTriggerSummaryData'
309+ iTriggerDetailsHtmlUrl = incident.iTriggerDetailsHtmlUrl'
310+
311+instance HasIncident Incident where
312+ incident = id
313+
314+data ListIncidents = ListIncidents
315+ { _lsSince' :: Maybe Date
316+ , _lsUntil' :: Maybe Date
317+ , _lsDateRange' :: Maybe Range
318+ , _lsStatus' :: Maybe (CSV IncidentStatus)
319+ , _lsIncidentKey' :: Maybe IncidentKey
320+ , _lsService' :: Maybe (CSV ServiceId)
321+ , _lsAssignedToUser' :: Maybe (CSV UserId)
322+ , _lsTimeZone' :: !TZ
323+ , _lsSortBy' :: Maybe (Sort Field)
324+ } deriving (Eq, Show)
325+
326+queryRequest ''ListIncidents
327+
328+instance Paginate ListIncidents
329+
330+-- | The start of the date range over which you want to search.
331+lsSince :: Lens' (Request ListIncidents s b) (Maybe UTCTime)
332+lsSince = upd.lsSince'.mapping _D
333+
334+-- | The end of the date range over which you want to search.
335+--
336+-- /Note: If you leave off either since or until, a 30 day default range is
337+-- applied to your open ended range. Not including the since parameter will set
338+-- the date range to until - 30 days. Likewise, if you leave off until,
339+-- it is set to since + 30 days.
340+--
341+-- Defaults to the last 30 days if you leave off both. The size of the date
342+-- range must be less than 180 days.
343+lsUntil :: Lens' (Request ListIncidents s b) (Maybe UTCTime)
344+lsUntil = upd.lsUntil'.mapping _D
345+
346+-- | When set, the since and until parameters and defaults are
347+-- ignored. Use this to get all incidents since the account was created.
348+lsDateRange :: Lens' (Request ListIncidents s b) Bool
349+lsDateRange = upd.lens get_ (flip set_)
350+ where
351+ get_ = (Just All ==) . view lsDateRange'
352+ set_ x = lsDateRange' .~ listToMaybe [All | x]
353+
354+-- | Returns only the incidents currently in the passed status(es).
355+lsStatus :: Lens' (Request ListIncidents s b) (Maybe [IncidentStatus])
356+lsStatus = upd.lsStatus'.mapping _CSV
357+
358+-- | Returns only the incidents with the passed de-duplication key.
359+--
360+-- /See:/ "Network.PagerDuty.Integration"
361+lsIncidentKey :: Lens' (Request ListIncidents s b) (Maybe IncidentKey)
362+lsIncidentKey = upd.lsIncidentKey'
363+
364+-- | Returns only the incidents associated with the passed service(s). This
365+-- expects one or more service IDs.
366+lsService :: Lens' (Request ListIncidents s b) (Maybe [ServiceId])
367+lsService = upd.lsService'.mapping _CSV
368+
369+-- | Returns only the incidents currently assigned to the passed user(s). This
370+-- expects one or more user IDs.
371+--
372+-- /Note:/ When using the assignedToUser filter, you will only receive
373+-- incidents with statuses of triggered or acknowledged. This is because resolved
374+-- incidents are not assigned to any user.
375+lsAssignedToUser :: Lens' (Request ListIncidents s b) (Maybe [UserId])
376+lsAssignedToUser = upd.lsAssignedToUser'.mapping _CSV
377+
378+-- | Time zone in which dates in the result will be rendered.
379+--
380+-- /Default:/ 'UTC'.
381+lsTimeZone :: Lens' (Request ListIncidents s b) TimeZone
382+lsTimeZone = upd.lsTimeZone'._TZ
383+
384+-- | Used to specify both the field you wish to sort the results on, as well as
385+-- the direction (ascending/descending) of the results.
386+lsSortBy :: Lens' (Request ListIncidents s b) (Maybe (Sort Field))
387+lsSortBy = upd.lsSortBy'
388+
389+-- | The PagerDuty incidents query API can be used to query current and
390+-- historical PagerDuty incidents over a date range, letting you build custom
391+-- dashboards or incident reports. The API allows for searching for incidents
392+-- with multiple filters or query parameters, various sorts, and also supports
393+-- the pagination of results.
394+--
395+-- @GET \/incidents@
396+--
397+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/list>
398+listIncidents :: Request ListIncidents s [Incident]
399+listIncidents =
400+ mk ListIncidents
401+ { _lsSince' = Nothing
402+ , _lsUntil' = Nothing
403+ , _lsDateRange' = Nothing
404+ , _lsStatus' = Nothing
405+ , _lsIncidentKey' = Nothing
406+ , _lsService' = Nothing
407+ , _lsAssignedToUser' = Nothing
408+ , _lsTimeZone' = def
409+ , _lsSortBy' = Nothing
410+ } & path .~ incidents
411+
412+-- | Get detailed information about an incident. Accepts either an incident id,
413+-- or an incident number.
414+--
415+-- @GET \/incidents\/\:id@
416+--
417+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/show>
418+getIncident :: IncidentId -> Request Empty s Incident
419+getIncident i = empty & path .~ incidents % i
420+
421+data CountIncidents = CountIncidents
422+ { _cSince' :: Maybe Date
423+ , _cUntil' :: Maybe Date
424+ , _cDateRange' :: Maybe Range
425+ , _cStatus' :: Maybe (CSV IncidentStatus)
426+ , _cIncidentKey' :: Maybe IncidentKey
427+ , _cService' :: Maybe (CSV ServiceId)
428+ , _cAssignedToUser' :: Maybe (CSV UserId)
429+ } deriving (Eq, Show)
430+
431+queryRequest ''CountIncidents
432+
433+-- | The start of the date range over which you want to search.
434+cSince :: Lens' (Request CountIncidents s b) (Maybe UTCTime)
435+cSince = upd.cSince'.mapping _D
436+
437+-- | The end of the date range over which you want to search.
438+--
439+-- /Note: If you leave off either since or until, a 30 day default range is
440+-- applied to your open ended range. Not including the since parameter will set
441+-- the date range to until - 30 days. Likewise, if you leave off until,
442+-- it is set to since + 30 days.
443+--
444+-- Defaults to the last 30 days if you leave off both. The size of the date
445+-- range must be less than 180 days.
446+cUntil :: Lens' (Request CountIncidents s b) (Maybe UTCTime)
447+cUntil = upd.cUntil'.mapping _D
448+
449+-- | When set, the since and until parameters and defaults are
450+-- ignored. Use this to get all counts since the account was created.
451+cDateRange :: Lens' (Request CountIncidents s b) Bool
452+cDateRange = upd.lens get_ (flip set_)
453+ where
454+ get_ = (Just All ==) . view cDateRange'
455+ set_ x = cDateRange' .~ listToMaybe [All | x]
456+
457+-- | Only counts the incidents currently in the passed status(es).
458+cStatus :: Lens' (Request CountIncidents s b) (Maybe [IncidentStatus])
459+cStatus = upd.cStatus'.mapping _CSV
460+
461+-- | Returns only the incidents with the passed de-duplication key.
462+--
463+-- /See:/ "Network.PagerDuty.Integration"
464+cIncidentKey :: Lens' (Request CountIncidents s b) (Maybe IncidentKey)
465+cIncidentKey = upd.cIncidentKey'
466+
467+-- | Only counts the incidents associated with the passed service(s).
468+-- This is expecting one or more service IDs.
469+cService :: Lens' (Request CountIncidents s b) (Maybe [ServiceId]) -- list1
470+cService = upd.cService'.mapping _CSV
471+
472+-- | Only counts the incidents currently assigned to the passed user(s).
473+-- This is expecting one or more user IDs.
474+--
475+-- /Note:/ When using the assigned_to_user filter, you will only count
476+-- incidents with statuses of triggered or acknowledged. This is because resolved
477+-- incidents are not assigned to any user.
478+cAssignedToUser :: Lens' (Request CountIncidents s b) (Maybe [UserId]) -- list1
479+cAssignedToUser = upd.cAssignedToUser'.mapping _CSV
480+
481+newtype Count = Count Int
482+ deriving (Eq, Ord, Show, Bounded, Enum, Num, Integral, Real)
483+
484+instance FromJSON Count where
485+ parseJSON = withObject "count" $ \o -> Count <$> o .: "total"
486+
487+-- | Use this query if you are simply looking for the count of incidents that
488+-- match a given query. This should be used if you don't need access to the
489+-- actual incident details.
490+--
491+-- @GET \/incidents\/count@
492+--
493+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/count>
494+countIncidents :: Request CountIncidents s Count
495+countIncidents =
496+ mk CountIncidents
497+ { _cSince' = Nothing
498+ , _cUntil' = Nothing
499+ , _cDateRange' = Nothing
500+ , _cStatus' = Nothing
501+ , _cIncidentKey' = Nothing
502+ , _cService' = Nothing
503+ , _cAssignedToUser' = Nothing
504+ } & path .~ incidents % "count"
505+
506+
507+data UpdateStatus
508+ = UAcknowledged
509+ | UResolved
510+ deriving (Eq, Show)
511+
512+deriveNullary ''UpdateStatus
513+
514+data UpdateIncident = UpdateIncident
515+ { _iiId :: IncidentId
516+ , _iiStatus :: Maybe UpdateStatus
517+ , _iiEscalationLevel :: Maybe Int
518+ , _iiEscalationPolicy :: Maybe EscalationPolicyId
519+ , _iiAssignedToUser :: Maybe (CSV UserId)
520+ } deriving (Eq, Show)
521+
522+deriveJSON ''UpdateIncident
523+
524+-- | The id of the incident to update.
525+makeLens "_iiId" ''UpdateIncident
526+
527+-- | The new status of the incident.
528+makeLens "_iiStatus" ''UpdateIncident
529+
530+-- | Escalate incident to this level in the escalation policy.
531+makeLens "_iiEscalationLevel" ''UpdateIncident
532+
533+-- | Delegate this incident to the specified escalation policy id.
534+-- This restarts the incident's escalation following the new policy.
535+makeLens "_iiEscalationPolicy" ''UpdateIncident
536+
537+-- | List of user IDs to assign this incident to.
538+makeLens "_iiAssignedToUser" ''UpdateIncident
539+
540+data UpdatedIncidents = UpdatedIncidents
541+ { _uiId :: IncidentId
542+ , _uiStatus :: IncidentStatus
543+ , _uiIncidentNumber :: !Int
544+ , _uiUrl :: Text
545+ , _uiError :: Maybe Object
546+ } deriving (Eq, Show)
547+
548+deriveRecord ''UpdatedIncidents
549+
550+data UpdateIncidents = UpdateIncidents
551+ { _uiIncidents' :: [UpdateIncident]
552+ } deriving (Eq, Show)
553+
554+jsonRequest ''UpdateIncidents
555+
556+uiIncidents :: Lens' (Request UpdateIncidents s b) [UpdateIncident]
557+uiIncidents = upd.uiIncidents'
558+
559+-- | Acknowledge, resolve, escalate or reassign one or more incidents.
560+--
561+-- @PUT \/incidents@
562+--
563+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/update>
564+updateIncidents :: RequesterId -> Request UpdateIncidents s [UpdatedIncidents]
565+updateIncidents r = auth updateIncidentsBasic & query .~ [("requester_id", r)]
566+
567+-- | A version of 'updateIncidents' which uses HTTP Basic authentication and
568+-- doesn't require a 'RequesterId'.
569+updateIncidentsBasic :: Request UpdateIncidents Basic [UpdatedIncidents]
570+updateIncidentsBasic =
571+ mk UpdateIncidents
572+ { _uiIncidents' = []
573+ } & meth .~ PUT
574+ & path .~ incidents
575+
576+-- | Resolve an incident.
577+--
578+-- @PUT \/incidents\/\:id\/resolve@
579+--
580+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/resolve>
581+resolveIncident :: IncidentId
582+ -> RequesterId
583+ -> Request Empty s Empty
584+resolveIncident i r =
585+ auth (resolveIncidentBasic i) & query .~ [("requester_id", r)]
586+
587+-- | A version of 'resolveIncident' which uses HTTP Basic authentication and
588+-- doesn't require a 'RequesterId'.
589+resolveIncidentBasic :: IncidentId -> Request Empty s Empty
590+resolveIncidentBasic i = empty & meth .~ PUT & path .~ incidents % i % "resolve"
591+
592+-- | Acknowledge an incident.
593+--
594+-- @PUT \/incidents\/\:id\/acknowledge@
595+--
596+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/acknowledge>
597+acknowledgeIncident :: IncidentId
598+ -> RequesterId
599+ -> Request Empty s Empty
600+acknowledgeIncident i r =
601+ auth (acknowledgeIncidentBasic i) & query .~ [("requester_id", r)]
602+
603+-- | A version of 'acknowledgeIncident' which uses HTTP Basic authentication and
604+-- doesn't require a 'RequesterId'.
605+acknowledgeIncidentBasic :: IncidentId -> Request Empty s Empty
606+acknowledgeIncidentBasic i =
607+ empty & meth .~ PUT & path .~ incidents % i % "acknowledge"
608+
609+data ReassignIncident = ReassignIncident
610+ { _riEscalationPolicy' :: Maybe EscalationPolicyId
611+ , _riEscalationLevel' :: Maybe Int
612+ , _riAssignedToUser' :: Maybe (CSV UserId)
613+ } deriving (Eq, Show)
614+
615+jsonRequest ''ReassignIncident
616+
617+-- | The ID of an escalation policy. Delegate the incident to this escalation
618+-- policy.
619+riEscalationPolicy :: Lens' (Request ReassignIncident s b) (Maybe EscalationPolicyId)
620+riEscalationPolicy = upd.riEscalationPolicy'
621+
622+-- | Escalate incident to this level in the escalation policy.
623+riEscalationLevel :: Lens' (Request ReassignIncident s b) (Maybe Int)
624+riEscalationLevel = upd.riEscalationLevel'
625+
626+-- | Comma separated list of user IDs to assign this incident to.
627+riAssignedToUser :: Lens' (Request ReassignIncident s b) (Maybe [UserId])
628+riAssignedToUser = upd.riAssignedToUser'.mapping _CSV
629+
630+-- | Reassign an incident.
631+--
632+-- @PUT \/incidents\/\:id\/reassign@
633+--
634+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/reassign>
635+reassignIncident :: IncidentId
636+ -> RequesterId
637+ -> Request ReassignIncident s Empty
638+reassignIncident i r =
639+ auth (reassignIncidentBasic i) & query .~ [("requester_id", r)]
640+
641+-- | A version of 'reassignIncident' which uses HTTP Basic authentication and
642+-- doesn't require a 'RequesterId'.
643+reassignIncidentBasic :: IncidentId -> Request ReassignIncident s Empty
644+reassignIncidentBasic i =
645+ mk ReassignIncident
646+ { _riEscalationPolicy' = Nothing
647+ , _riEscalationLevel' = Nothing
648+ , _riAssignedToUser' = Nothing
649+ } & meth .~ PUT
650+ & path .~ incidents % i % "reassign"
src/Network/PagerDuty/REST/Incidents/Notes.hsnew file+108
@@ -0,0 +1,108 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
6+
7+-- Module : Network.PagerDuty.REST.Incidents.Notes
8+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9+-- License : This Source Code Form is subject to the terms of
10+-- the Mozilla Public License, v. 2.0.
11+-- A copy of the MPL can be found in the LICENSE file or
12+-- you can obtain it at http://mozilla.org/MPL/2.0/.
13+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14+-- Stability : experimental
15+-- Portability : non-portable (GHC extensions)
16+
17+-- | The Incident Notes API allows you to add notes to a specified incident.
18+--
19+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/notes>
20+module Network.PagerDuty.REST.Incidents.Notes
21+ (
22+ -- * List Notes
23+ listNotes
24+
25+ -- * Create Note
26+ , CreateNote
27+ , createNote
28+ , createNoteBasic
29+ , cnContent
30+
31+ -- * Types
32+ , Note
33+ , nId
34+ , nCreatedAt
35+ , nUser
36+ , nContent
37+ ) where
38+
39+import Control.Lens hiding ((.=))
40+import Data.Aeson
41+import Data.Text (Text)
42+import Data.Time
43+import Network.HTTP.Types
44+import Network.PagerDuty.REST.Users
45+import Network.PagerDuty.Internal.TH
46+import Network.PagerDuty.Internal.Types
47+
48+default (Path)
49+
50+notes :: IncidentId -> Path
51+notes i = "incidents" % i % "notes"
52+
53+data Note = Note
54+ { _nId :: NoteId
55+ , _nCreatedAt :: Date
56+ , _nUser :: User
57+ , _nContent :: Text
58+ } deriving (Eq, Show)
59+
60+deriveJSON ''Note
61+
62+makeLens "_nId" ''Note
63+makeLens "_nUser" ''Note
64+makeLens "_nContent" ''Note
65+
66+nCreatedAt :: Lens' Note UTCTime
67+nCreatedAt = lens _nCreatedAt (\n x -> n { _nCreatedAt = x }) . _D
68+
69+-- | List existing notes for the specified incident.
70+--
71+-- @GET \/incidents\/\:incident_id\/notes@
72+--
73+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/notes/create>
74+listNotes :: IncidentId -> Request Empty s [Note]
75+listNotes i = empty & path .~ notes i
76+
77+newtype CreateNote = CreateNote
78+ { _cnContent' :: Maybe Text
79+ } deriving (Eq, Show)
80+
81+makeLenses ''CreateNote
82+
83+instance ToJSON CreateNote where
84+ toJSON cn = object ["note" .= object ["content" .= _cnContent' cn]]
85+
86+instance QueryLike CreateNote where
87+ toQuery = const []
88+
89+-- | The note content.
90+cnContent :: Lens' (Request CreateNote s b) (Maybe Text)
91+cnContent = upd.cnContent'
92+
93+-- | Create a new note for the specified incident.
94+--
95+-- @POST \/incidents\/\:incident_id\/notes@
96+--
97+-- /See:/ <http://developer.pagerduty.com/documentation/rest/incidents/notes/create>
98+createNote :: IncidentId -> RequesterId -> Request CreateNote s Note
99+createNote i r = auth (createNoteBasic i) & query .~ [("requester_id", r)]
100+
101+-- | A version of 'createNote' which uses HTTP Basic authentication and
102+-- doesn't require a 'RequesterId'.
103+createNoteBasic :: IncidentId -> Request CreateNote s Note
104+createNoteBasic i =
105+ mk CreateNote
106+ { _cnContent' = Nothing
107+ } & meth .~ POST
108+ & path .~ notes i
src/Network/PagerDuty/REST/LogEntries.hsnew file+514
@@ -0,0 +1,514 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE StandaloneDeriving #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+-- Module : Network.PagerDuty.REST.LogEntries
8+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9+-- License : This Source Code Form is subject to the terms of
10+-- the Mozilla Public License, v. 2.0.
11+-- A copy of the MPL can be found in the LICENSE file or
12+-- you can obtain it at http://mozilla.org/MPL/2.0/.
13+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14+-- Stability : experimental
15+-- Portability : non-portable (GHC extensions)
16+
17+-- | PagerDuty keeps a log of all the events that happen to an incident. The
18+-- following APIs provide fine-grained access to this incident log entry data to
19+-- give you more insight into how your team or organization is handling your
20+-- incidents. Log entry data includes details about the event(s) that triggered
21+-- the incident, who was notified and when, how they were notified, and who
22+-- acknowledged or resolved it, amongst a few other things.
23+--
24+-- /See:/ <http://developer.pagerduty.com/documentation/rest/log_entries>
25+module Network.PagerDuty.REST.LogEntries
26+ (
27+ -- * List Logs
28+ ListLogs
29+ , listLogs
30+ , listUserLogs
31+ , listIncidentLogs
32+ , llTimeZone
33+ , llSince
34+ , llUntil
35+ , llIsOverview
36+
37+ -- * Get Log
38+ , GetLog
39+ , getLog
40+ , glTimeZone
41+
42+ -- * Types
43+ -- ** Channels
44+ , Channel (..)
45+ , _ChanNagios
46+ , _ChanAPI
47+ , _ChanEmail
48+ , _ChanUserEmail
49+ , _ChanWebTrigger
50+ , _ChanSMS
51+ , _ChanWeb
52+ , _ChanNote
53+ , _ChanAuto
54+ , _ChanTimeout
55+
56+ , NagiosChannel
57+ , ncSummary
58+ , ncHost
59+ , ncService
60+ , ncState
61+ , ncDetails
62+
63+ , APIChannel
64+ , acSummary
65+ , acServiceKey
66+ , acDescription
67+ , acIncidentKey
68+ , acDetails
69+
70+ , EmailType (..)
71+ , EmailChannel
72+ , ecSummary
73+ , ecTo
74+ , ecFrom
75+ , ecSubject
76+ , ecBody
77+ , ecBodyContentType
78+ , ecRawUrl
79+ , ecHtmlUrl
80+
81+ , WebTriggerChannel
82+ , wcSummary
83+ , wcSubject
84+ , wcDetails
85+
86+ -- ** Entries
87+ , Entry (..)
88+ , _LogEntry
89+ , _NotifyEntry
90+
91+ , LogEntryType (..)
92+ , LogEntry
93+ , leId
94+ , leType
95+ , leCreatedAt
96+ , leNote
97+ , leAssignedUser
98+ , leChannel
99+
100+ , NotifyEntry
101+ , neCreatedAt
102+ , neUser
103+ , neNotification
104+
105+ , NotificationType (..)
106+ , NotificationStatus (..)
107+
108+ , Notification
109+ , nType
110+ , nStatus
111+ , nAddress
112+ ) where
113+
114+import Control.Applicative
115+import Control.Lens
116+import Data.Aeson
117+import Data.Aeson.Lens
118+import Data.Default.Class
119+import qualified Data.HashMap.Strict as Map
120+import Data.Text (Text)
121+import qualified Data.Text as Text
122+import Data.Time
123+import Network.HTTP.Types
124+import Network.PagerDuty.REST.Users (User)
125+import Network.PagerDuty.Internal.TH
126+import Network.PagerDuty.Internal.Types
127+
128+logs :: Path
129+logs = "log_entries"
130+
131+includes :: Query
132+includes =
133+ [ ("include[]", Just "channel")
134+ , ("include[]", Just "incident")
135+ , ("include[]", Just "service")
136+ ]
137+
138+data NagiosChannel = NagiosChannel
139+ { _ncSummary :: Text
140+ , _ncHost :: Text
141+ , _ncService :: Maybe Text
142+ , _ncState :: Text
143+ , _ncDetails :: Object
144+ } deriving (Eq, Show)
145+
146+deriveJSON ''NagiosChannel
147+
148+-- | Same as host.
149+makeLens "_ncSummary" ''NagiosChannel
150+
151+-- | Nagios host.
152+makeLens "_ncHost" ''NagiosChannel
153+
154+-- | Nagios service that created the event, if applicable.
155+makeLens "_ncService" ''NagiosChannel
156+
157+-- | State the caused the event.
158+makeLens "_ncState" ''NagiosChannel
159+
160+-- | Additional details of the incident.
161+makeLens "_ncDetails" ''NagiosChannel
162+
163+data APIChannel = APIChannel
164+ { _acSummary :: Text
165+ , _acServiceKey :: ServiceKey
166+ , _acDescription :: Text
167+ , _acIncidentKey :: IncidentKey
168+ , _acDetails :: Object
169+ } deriving (Eq, Show)
170+
171+deriveJSON ''APIChannel
172+
173+-- | Same as description.
174+makeLens "_acSummary" ''APIChannel
175+
176+-- | API service key.
177+makeLens "_acServiceKey" ''APIChannel
178+
179+-- | Description of the event.
180+makeLens "_acDescription" ''APIChannel
181+
182+-- | Incident deduping string.
183+makeLens "_acIncidentKey" ''APIChannel
184+
185+-- | Additional details of the incident, if any.
186+makeLens "_acDetails" ''APIChannel
187+
188+data EmailType
189+ = Plain
190+ | HTML
191+ deriving (Eq, Show)
192+
193+deriveNullary ''EmailType
194+
195+data EmailChannel = EmailChannel
196+ { _ecSummary :: Text
197+ , _ecTo :: Text
198+ , _ecFrom :: Text
199+ , _ecSubject :: Text
200+ , _ecBody :: Text
201+ , _ecBodyContentType :: EmailType
202+ , _ecRawUrl :: Text
203+ , _ecHtmlUrl :: Maybe Text
204+ } deriving (Eq, Show)
205+
206+deriveJSON ''EmailChannel
207+
208+-- | Same as subject.
209+makeLens "_ecSummary" ''EmailChannel
210+
211+-- | To address of the email.
212+makeLens "_ecTo" ''EmailChannel
213+
214+-- | From address of the email.
215+makeLens "_ecFrom" ''EmailChannel
216+
217+-- | Subject of the email.
218+makeLens "_ecSubject" ''EmailChannel
219+
220+-- | Body of the email.
221+makeLens "_ecBody" ''EmailChannel
222+
223+-- | Content type of the email body.
224+makeLens "_ecBodyContentType" ''EmailChannel
225+
226+-- | URL for raw text of email.
227+makeLens "_ecRawUrl" ''EmailChannel
228+
229+-- | URL for html rendered version of the email.
230+--
231+-- Only present if content_type is 'HTML'.
232+makeLens "_ecHtmlUrl" ''EmailChannel
233+
234+data WebTriggerChannel = WebTriggerChannel
235+ { _wcSummary :: Text
236+ , _wcSubject :: Text
237+ , _wcDetails :: Maybe Text
238+ } deriving (Eq, Show)
239+
240+deriveJSON ''WebTriggerChannel
241+
242+-- | Same as subject.
243+makeLens "_wcSummary" ''WebTriggerChannel
244+
245+-- | Subject of the web trigger.
246+makeLens "_wcSubject" ''WebTriggerChannel
247+
248+-- | Details about the web trigger.
249+makeLens "_wcDetails" ''WebTriggerChannel
250+
251+-- | Representation of the means by which the action was channeled.
252+data Channel
253+ = ChanNagios NagiosChannel
254+ | ChanAPI APIChannel
255+ | ChanEmail EmailChannel
256+ | ChanUserEmail User EmailChannel
257+ | ChanWebTrigger User WebTriggerChannel
258+ | ChanSMS User
259+ | ChanWeb User
260+ | ChanNote User
261+ | ChanAuto
262+ | ChanTimeout
263+ deriving (Eq, Show)
264+
265+makePrisms ''Channel
266+
267+instance FromJSON Channel where
268+ parseJSON = withObject "agent+channel" $ \o -> do
269+ t <- o .: "channel" >>= (.: "type")
270+ let u = Map.member "user" o
271+ case t of
272+ -- service
273+ "email" | not u -> ChanEmail <$> o .: "channel"
274+ "nagios" -> ChanNagios <$> o .: "channel"
275+ "api" -> ChanAPI <$> o .: "channel"
276+ -- user
277+ "email" | u -> ChanUserEmail <$> o .: "user" <*> o .: "channel"
278+ "web_trigger" -> ChanWebTrigger <$> o .: "user" <*> o .: "channel"
279+ "sms" -> ChanSMS <$> o .: "user"
280+ "web" -> ChanWeb <$> o .: "user"
281+ "note" -> ChanNote <$> o .: "user"
282+ -- empty
283+ "auto" -> pure ChanAuto
284+ "timeout" -> pure ChanTimeout
285+ _ -> fail $
286+ "unrecognised service channel type: " ++ Text.unpack t
287+
288+data LogEntryType
289+ = Trigger
290+ -- ^ The incident was triggered.
291+ | Acknowledge
292+ -- ^ The incident was acknowledged.
293+ | Unacknowledge
294+ -- ^ The incident was unacknowledged.
295+ | Resolve
296+ -- ^ The incident was resolved.
297+ | Escalate
298+ -- ^ The incident was escalated.
299+ | Assign
300+ -- ^ The incident was assigned to a user.
301+ | Annotate
302+ -- ^ A note was added to the incident.
303+ | ReachTriggerLimit
304+ -- ^ The incident has reached the log entry trigger limit and
305+ -- will not create any more.
306+ | RepeatEscalationPath
307+ -- ^ The incident has reached the end of its escalation policy
308+ -- and will restart.
309+ | ExhaustEscalationPath
310+ -- ^ The incident has cycled through its escalation policy the
311+ -- max allowed number of times.
312+ | Notify
313+ -- ^ A user was notified.
314+ deriving (Eq, Show)
315+
316+deriveNullary ''LogEntryType
317+
318+-- | The raw log event.
319+data LogEntry = LogEntry'
320+ { _leId :: LogEntryId
321+ , _leType :: LogEntryType
322+ , _leCreatedAt :: Date
323+ , _leNote :: Maybe Text
324+ , _leAssignedUser :: Maybe User
325+ , _leChannel :: Channel
326+ } deriving (Eq, Show)
327+
328+instance FromJSON LogEntry where
329+ parseJSON = withObject "log_entry" $ \o ->
330+ LogEntry' <$> o .: "id"
331+ <*> o .: "type"
332+ <*> o .: "created_at"
333+ <*> o .:? "note"
334+ <*> o .:? "assigned_user"
335+ <*> parseJSON (Object o)
336+
337+-- | Id of the log entry.
338+makeLens "_leId" ''LogEntry
339+
340+-- | Time at which the log entry was created.
341+makeLens "_leCreatedAt" ''LogEntry
342+
343+-- | The type of the log entry.
344+makeLens "_leType" ''LogEntry
345+
346+-- | Optional field containing an action note, if one was included
347+-- with the action.
348+makeLens "_leNote" ''LogEntry
349+
350+-- | Only for assign, escalate log entries. The user to which the incident is
351+-- assigned.
352+makeLens "_leAssignedUser" ''LogEntry
353+
354+-- | Representation of the means by which the action was channeled and the
355+-- possible perfomer of this action.
356+makeLens "_leChannel" ''LogEntry
357+
358+data NotificationType
359+ = SMS
360+ | Email
361+ | Phone
362+ | IosPushNotification
363+ deriving (Eq, Show)
364+
365+deriveNullary ''NotificationType
366+
367+data NotificationStatus
368+ = Success
369+ | Blocked
370+ | Busy
371+ | Failed
372+ | NoAnswer
373+ | BadAddress
374+ deriving (Eq, Show)
375+
376+deriveNullary ''NotificationStatus
377+
378+data Notification = Notification
379+ { _nType :: !NotificationType
380+ , _nStatus :: !NotificationStatus
381+ , _nAddress :: Address
382+ } deriving (Eq, Show)
383+
384+deriveJSON ''Notification
385+
386+-- | Type of notification.
387+makeLens "_nType" ''Notification
388+
389+-- | The current status of the notification.
390+makeLens "_nStatus" ''Notification
391+
392+-- | The address to which the notification was sent. I.e., an email address,
393+-- phone number, or iPhone name.
394+makeLens "_nAddress" ''Notification
395+
396+-- | Notify log entries correspond to notifications sent to users. They have a
397+-- distinct format from action log entries.
398+data NotifyEntry = NotifyEntry'
399+ { _neCreatedAt' :: Date
400+ , _neUser' :: User
401+ , _neNotification' :: Notification
402+ } deriving (Eq, Show)
403+
404+deriveRecord ''NotifyEntry
405+
406+-- | Time at which the log entry was created
407+neCreatedAt :: Lens' NotifyEntry UTCTime
408+neCreatedAt = neCreatedAt'._D
409+
410+-- | User who was notified
411+neUser :: Lens' NotifyEntry User
412+neUser = neUser'
413+
414+-- | Object representing the notification itself
415+neNotification :: Lens' NotifyEntry Notification
416+neNotification = neNotification'
417+
418+data Entry
419+ = LogEntry LogEntry
420+ -- ^ Log entries come in a wide variety of types. Most types use this format,
421+ -- the exception being the 'NotifyEntry' type.
422+ | NotifyEntry NotifyEntry
423+ -- ^ A user was notified.
424+ deriving (Eq, Show)
425+
426+makePrisms ''Entry
427+
428+data ListLogs = ListLogs
429+ { _llTimeZone' :: TZ
430+ , _llSince' :: Maybe Date
431+ , _llUntil' :: Maybe Date
432+ , _llIsOverview' :: Bool'
433+ } deriving (Eq, Show)
434+
435+instance Paginate ListLogs
436+
437+queryRequest ''ListLogs
438+
439+-- | Time zone in which dates in the result will be rendered.
440+--
441+-- /Default:/ UTC.
442+llTimeZone :: Lens' (Request ListLogs s b) TimeZone
443+llTimeZone = upd.llTimeZone'._TZ
444+
445+-- | The start of the date range over which you want to search.
446+llSince :: Lens' (Request ListLogs s b) (Maybe UTCTime)
447+llSince = upd.llSince'.mapping _D
448+
449+-- | The end of the date range over which you want to search.
450+llUntil :: Lens' (Request ListLogs s b) (Maybe UTCTime)
451+llUntil = upd.llUntil'.mapping _D
452+
453+-- | If true, will only return log entries of type trigger, acknowlldge, or resolve.
454+--
455+-- /Default:/ false.
456+llIsOverview :: Lens' (Request ListLogs s b) Bool
457+llIsOverview = upd.llIsOverview'._B
458+
459+-- | List all incident log entries across the entire account.
460+--
461+-- @GET \/log_entries@
462+--
463+-- /See:/ <http://developer.pagerduty.com/documentation/rest/log_entries/list>
464+listLogs :: Request ListLogs s [LogEntry]
465+listLogs =
466+ mk ListLogs
467+ { _llTimeZone' = def
468+ , _llSince' = Nothing
469+ , _llUntil' = Nothing
470+ , _llIsOverview' = F
471+ } & path .~ logs
472+ & query .~ includes
473+ & unwrap .~ key "log_entries"
474+
475+-- | List all incident log entries that describe interactions with a specific user.
476+--
477+-- @GET \/users\/\:user_id\/log_entries@
478+--
479+-- /See:/ <http://developer.pagerduty.com/documentation/rest/log_entries/user_log_entries>
480+listUserLogs :: UserId -> Request ListLogs s [LogEntry]
481+listUserLogs u = listLogs & path .~ "users" % u % logs
482+
483+-- | List all incident log entries for a specific incident.
484+--
485+-- @GET \/incidents\/\:incident_id\/log_entries@
486+--
487+-- /See:/ <http://developer.pagerduty.com/documentation/rest/log_entries/incident_log_entries>
488+listIncidentLogs :: IncidentKey -> Request ListLogs s [LogEntry]
489+listIncidentLogs i = listLogs & path .~ "incidents" % i % logs
490+
491+newtype GetLog = GetLog
492+ { _glTimeZone' :: TZ
493+ } deriving (Eq, Show)
494+
495+queryRequest ''GetLog
496+
497+-- | Time zone in which dates in the result will be rendered.
498+--
499+-- /Default:/ UTC.
500+glTimeZone :: Lens' (Request GetLog s b) TimeZone
501+glTimeZone = upd.glTimeZone'._TZ
502+
503+-- | Get details for a specific incident log entry. This method provides additional
504+-- information you can use to get at raw event data.
505+--
506+-- @GET \/log_entries\/\:id@
507+--
508+-- /See:/ <http://developer.pagerduty.com/documentation/rest/log_entries/show>
509+getLog :: LogEntryId -> Request GetLog s LogEntry
510+getLog l =
511+ mk GetLog
512+ { _glTimeZone' = def
513+ } & path .~ logs % l
514+ & query .~ includes
src/Network/PagerDuty/REST/MaintenanceWindows.hsnew file+289
@@ -0,0 +1,289 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+{-# LANGUAGE TypeFamilies #-}
5+
6+-- Module : Network.PagerDuty.REST.MaintenanceWindows
7+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
8+-- License : This Source Code Form is subject to the terms of
9+-- the Mozilla Public License, v. 2.0.
10+-- A copy of the MPL can be found in the LICENSE file or
11+-- you can obtain it at http://mozilla.org/MPL/2.0/.
12+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
13+-- Stability : experimental
14+-- Portability : non-portable (GHC extensions)
15+
16+-- | Maintenance windows allow you to schedule service maintenance periods,
17+-- during which no incidents will be created.
18+--
19+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows>
20+module Network.PagerDuty.REST.MaintenanceWindows
21+ (
22+ -- * List Windows
23+ ListWindows
24+ , listWindows
25+ , lwQuery
26+ , lwServiceIds
27+ , lwFilter
28+
29+ , ListWindowsResponse
30+ , lwMaintenanceWindows
31+ , lwCounts
32+
33+ -- * Create Window
34+ , CreateWindow
35+ , createWindow
36+ , createWindowBasic
37+ , cwStartTime
38+ , cwEndTime
39+ , cwDescription
40+ , cwServiceIds
41+
42+ -- * Get Window
43+ , getWindow
44+
45+ -- * Update Window
46+ , UpdateWindow
47+ , updateWindow
48+ , uwStartTime
49+ , uwEndTime
50+ , uwDescription
51+ , uwServiceIds
52+
53+ -- * Delete Window
54+ , deleteWindow
55+
56+ -- * Types
57+ , ServiceInfo
58+ , siId
59+ , siName
60+ , siUrl
61+
62+ , Counts
63+ , cOngoing
64+ , cFuture
65+ , cPast
66+ , cAll
67+
68+ , MaintenanceWindow
69+ , mwSequenceNumber
70+ , mwStartTime
71+ , mwEndTime
72+ , mwDescription
73+ , mwCreatedBy
74+ , mwServices
75+ ) where
76+
77+import Control.Lens
78+import Data.Monoid
79+import Data.Text (Text)
80+import Data.Time
81+import Network.HTTP.Types
82+import Network.PagerDuty.REST.Users (User)
83+import Network.PagerDuty.Internal.TH
84+import Network.PagerDuty.Internal.Types
85+
86+windows :: Path
87+windows = "maintenance_windows"
88+
89+data ServiceInfo = ServiceInfo
90+ { _siId :: ServiceId
91+ , _siName :: Text
92+ , _siUrl :: Text
93+ } deriving (Eq, Show)
94+
95+deriveRecord ''ServiceInfo
96+
97+data MaintenanceWindow = MaintenanceWindow
98+ { _mwSequenceNumber :: !Int
99+ , _mwStartTime :: Date
100+ , _mwEndTime :: Date
101+ , _mwDescription :: Maybe Text
102+ , _mwCreatedBy :: User
103+ , _mwServices :: [ServiceInfo]
104+ } deriving (Eq, Show)
105+
106+deriveRecord ''MaintenanceWindow
107+
108+data ListWindows = ListWindows
109+ { _lwQuery' :: Maybe Text
110+ , _lwServiceIds' :: List ServiceId
111+ , _lwFilter' :: Maybe Text
112+ }
113+
114+queryRequest ''ListWindows
115+
116+-- | Filters the results, showing only the maintenance windows whose
117+-- descriptions contain the query.
118+lwQuery :: Lens' (Request ListWindows s b) (Maybe Text)
119+lwQuery = upd.lwQuery'
120+
121+-- | An list of service IDs, specifying services whose maintenance
122+-- windows shall be returned.
123+lwServiceIds :: Lens' (Request ListWindows s b) [ServiceId]
124+lwServiceIds = upd.lwServiceIds'._L
125+
126+-- | Only return maintenance windows that are of this type. Possible values
127+-- are past, future, ongoing. If this parameter is omitted, all maintenance
128+-- windows will be returned.
129+lwFilter :: Lens' (Request ListWindows s b) (Maybe Text)
130+lwFilter = upd.lwFilter'
131+
132+data Counts = Counts
133+ { _cOngoing :: !Int
134+ , _cFuture :: !Int
135+ , _cPast :: !Int
136+ , _cAll :: !Int
137+ } deriving (Eq, Show)
138+
139+deriveRecord ''Counts
140+
141+data ListWindowsResponse = ListWindowsResponse
142+ { _lwMaintenanceWindows :: [MaintenanceWindow]
143+ , _lwCounts :: Counts
144+ } deriving (Eq, Show)
145+
146+deriveRecord ''ListWindowsResponse
147+
148+-- | List existing maintenance windows, optionally filtered by service,
149+-- or whether they are from the past, present or future.
150+--
151+-- @GET \/maintenance_windows@
152+--
153+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows/list>
154+listWindows :: Request ListWindows s ListWindowsResponse
155+listWindows =
156+ mk ListWindows
157+ { _lwQuery' = Nothing
158+ , _lwServiceIds' = mempty
159+ , _lwFilter' = Nothing
160+ } & path .~ windows
161+
162+data CreateWindow' = CreateWindow'
163+ { _cwStartTime' :: Date
164+ , _cwEndTime' :: Date
165+ , _cwDescription' :: Maybe Text
166+ , _cwServiceIds' :: [ServiceId] -- ^ List1
167+ } deriving (Eq, Show)
168+
169+deriveRecord ''CreateWindow'
170+
171+data CreateWindow = CreateWindow
172+ { _cwRequesterId' :: Maybe RequesterId
173+ , _cwMaintenanceWindow' :: CreateWindow'
174+ } deriving (Eq, Show)
175+
176+jsonRequest ''CreateWindow
177+
178+-- | This maintenance window's start time. This is when the services will stop
179+-- creating incidents. If this date is in the past, it will be updated to be the
180+-- current time.
181+cwStartTime :: Lens' (Request CreateWindow s b) UTCTime
182+cwStartTime = upd.cwMaintenanceWindow'.cwStartTime'._D
183+
184+-- | This maintenance window's end time. This is when the services will start
185+-- creating incidents again. This date must be in the future and after the
186+-- start_time.
187+cwEndTime :: Lens' (Request CreateWindow s b) UTCTime
188+cwEndTime = upd.cwMaintenanceWindow'.cwEndTime'._D
189+
190+-- | A description for this maintenance window.
191+cwDescription :: Lens' (Request CreateWindow s b) (Maybe Text)
192+cwDescription = upd.cwMaintenanceWindow'.cwDescription'
193+
194+-- | The ids of the services that are affected by this maintenance window.
195+cwServiceIds :: Lens' (Request CreateWindow s b) [ServiceId]
196+cwServiceIds = upd.cwMaintenanceWindow'.cwServiceIds'
197+
198+-- | Create a new maintenance window for the specified services. No new
199+-- incidents will be created for a service that is currently in maintenance.
200+--
201+-- @POST \/maintenance_windows@
202+--
203+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows/create>
204+createWindow :: RequesterId
205+ -> UTCTime -- ^ 'cwStartTime'
206+ -> UTCTime -- ^ 'cwEndTime'
207+ -> [ServiceId] -- ^ 'cwServiceIds'
208+ -> Request CreateWindow s MaintenanceWindow
209+createWindow r s e xs =
210+ auth (createWindowBasic s e xs) & upd.cwRequesterId' ?~ r
211+
212+-- | A version of 'createWindow' which uses HTTP Basic authentication and
213+-- doesn't require a 'RequesterId'.
214+createWindowBasic :: UTCTime -- ^ 'cwStartTime'
215+ -> UTCTime -- ^ 'cwEndTime'
216+ -> [ServiceId] -- ^ 'cwServiceIds'
217+ -> Request CreateWindow Basic MaintenanceWindow
218+createWindowBasic s e xs =
219+ mk CreateWindow
220+ { _cwRequesterId' = Nothing
221+ , _cwMaintenanceWindow' = CreateWindow'
222+ { _cwStartTime' = D s
223+ , _cwEndTime' = D e
224+ , _cwDescription' = Nothing
225+ , _cwServiceIds' = xs
226+ }
227+ } & meth .~ POST
228+ & path .~ windows
229+
230+-- | Get details about an existing maintenance window.
231+--
232+-- @GET \/maintenance_windows\/\:id@
233+--
234+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows/show>
235+getWindow :: WindowId -> Request Empty s MaintenanceWindow
236+getWindow w = empty & path .~ windows % w
237+
238+data UpdateWindow = UpdateWindow
239+ { _uwStartTime' :: Maybe Date
240+ , _uwEndTime' :: Maybe Date
241+ , _uwDescription' :: Maybe Text
242+ , _uwServiceIds' :: Maybe [ServiceId]
243+ } deriving (Eq, Show)
244+
245+jsonRequest ''UpdateWindow
246+
247+-- | The maintenance window's start time. Can only be updated on future
248+-- maintenance windows. If the start_time is set to a date in the past, it will be
249+-- updated to the current date.
250+uwStartTime :: Lens' (Request UpdateWindow s b) (Maybe Date)
251+uwStartTime = upd.uwStartTime'
252+
253+-- | The maintenance window's end time. Can only be updated on ongoing and
254+-- future maintenance windows, and cannot be set to a value before start_time.
255+uwEndTime :: Lens' (Request UpdateWindow s b) (Maybe Date)
256+uwEndTime = upd.uwEndTime'
257+
258+-- | Description for this maintenance window. Can only be updated on ongoing
259+-- and future maintenance windows.
260+uwDescription :: Lens' (Request UpdateWindow s b) (Maybe Text)
261+uwDescription = upd.uwDescription'
262+
263+-- | Services that are affected by this maintenance window. Can only be updated
264+-- on future maintenance windows.
265+uwServiceIds :: Lens' (Request UpdateWindow s b) (Maybe [ServiceId])
266+uwServiceIds = upd.uwServiceIds'
267+
268+-- | Update an existing maintenance window.
269+--
270+-- @PUT \/maintenance_windows\/\:id@
271+--
272+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows/update>
273+updateWindow :: WindowId -> Request UpdateWindow s MaintenanceWindow
274+updateWindow w =
275+ mk UpdateWindow
276+ { _uwStartTime' = Nothing
277+ , _uwEndTime' = Nothing
278+ , _uwDescription' = Nothing
279+ , _uwServiceIds' = Nothing
280+ } & meth .~ PUT
281+ & path .~ windows % w
282+
283+-- | Cancel or delete an existing maintenance window.
284+--
285+-- @DELETE \/maintenance_windows\/\:id@
286+--
287+-- /See:/ <http://developer.pagerduty.com/documentation/rest/maintenance_windows/delete>
288+deleteWindow :: WindowId -> Request Empty s Empty
289+deleteWindow w = empty & meth .~ DELETE & path .~ windows % w
src/Network/PagerDuty/REST/Reports.hsnew file+188
@@ -0,0 +1,188 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.Reports
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | Access high level reports about alerts and incidents.
20+-- Useful for creating graphs.
21+--
22+-- /See:/ <http://developer.pagerduty.com/documentation/rest/reports>
23+module Network.PagerDuty.REST.Reports
24+ (
25+ -- * Alerts Per Time
26+ alertsPerTime
27+
28+ -- * Incidents Per Time
29+ , incidentsPerTime
30+
31+ -- * Types
32+ , Report
33+ , rSince
34+ , rUntil
35+ , rRollup
36+
37+ , Rollup (..)
38+
39+ , AlertReport
40+ , arStart
41+ , arEnd
42+ , arNumberOfAlerts
43+ , arNumberOfPhoneAlerts
44+ , arNumberOfSmsAlerts
45+ , arNumberOfEmailAlerts
46+
47+ , AlertTotals
48+ , atAlerts
49+ , atTotalNumberOfAlerts
50+ , atTotalNumberOfPhoneAlerts
51+ , atTotalNumberOfSmsAlerts
52+ , atTotalNumberOfEmailAlerts
53+ , atTotalNumberOfBillableAlerts
54+
55+ , IncidentReport
56+ , irStart
57+ , irEnd
58+ , irNumberOfIncidents
59+ ) where
60+
61+import Control.Lens
62+import Data.Aeson.Lens
63+import Data.Time
64+import Network.PagerDuty.Internal.TH
65+import Network.PagerDuty.Internal.Types
66+
67+default (Path)
68+
69+reports :: Path
70+reports = "reports"
71+
72+data Rollup
73+ = Daily
74+ | Weekly
75+ | Monthly
76+ deriving (Eq, Show)
77+
78+deriveNullary ''Rollup
79+
80+data Report = Report
81+ { _rSince' :: Date
82+ , _rUntil' :: Date
83+ , _rRollup' :: !Rollup
84+ } deriving (Eq, Show)
85+
86+queryRequest ''Report
87+
88+-- | Start of the date range over which you want to search. The time element
89+-- is optional.
90+rSince :: Lens' (Request Report s b) UTCTime
91+rSince = upd.rSince'._D
92+
93+-- | The end of the date range over which you want to search. This should be
94+-- in the same format as since.
95+rUntil :: Lens' (Request Report s b) UTCTime
96+rUntil = upd.rUntil'._D
97+
98+-- | Specifies the bucket duration for each summation.
99+--
100+-- /Default:/ monthly.
101+--
102+-- Example: A time window of two years (based on since and until) with a
103+-- rollup of monthly will result in 24 sets of data points being returned
104+-- (one for each month in the span).
105+rRollup :: Lens' (Request Report s b) Rollup
106+rRollup = upd.rRollup'
107+
108+data AlertReport = AlertReport
109+ { _arStart :: Date
110+ , _arEnd :: Date
111+ , _arNumberOfAlerts :: !Int
112+ , _arNumberOfPhoneAlerts :: !Int
113+ , _arNumberOfSmsAlerts :: !Int
114+ , _arNumberOfEmailAlerts :: !Int
115+ } deriving (Eq, Show)
116+
117+deriveJSON ''AlertReport
118+
119+arStart :: Lens' AlertReport UTCTime
120+arStart = lens _arStart (\r x -> r { _arStart = x }) ._D
121+
122+arEnd :: Lens' AlertReport UTCTime
123+arEnd = lens _arEnd (\r x -> r { _arEnd = x }) ._D
124+
125+makeLens "_arNumberOfAlerts" ''AlertReport
126+makeLens "_arNumberOfPhoneAlerts" ''AlertReport
127+makeLens "_arNumberOfSmsAlerts" ''AlertReport
128+makeLens "_arNumberOfEmailAlerts" ''AlertReport
129+
130+data AlertTotals = AlertTotals
131+ { _atAlerts :: [AlertReport]
132+ , _atTotalNumberOfAlerts :: !Int
133+ , _atTotalNumberOfPhoneAlerts :: !Int
134+ , _atTotalNumberOfSmsAlerts :: !Int
135+ , _atTotalNumberOfEmailAlerts :: !Int
136+ , _atTotalNumberOfBillableAlerts :: !Int
137+ } deriving (Eq, Show)
138+
139+deriveRecord ''AlertTotals
140+
141+-- | Get high level statistics about the number of alerts (SMSes, phone calls and
142+-- emails) sent for the desired time period, summed daily, weekly or monthly.
143+--
144+-- @GET \/reports\/alerts_per_time@
145+--
146+-- /See:/ <http://developer.pagerduty.com/documentation/rest/reports/alerts_per_time>
147+alertsPerTime :: UTCTime -- ^ 'rSince'
148+ -> UTCTime -- ^ 'rUntil'
149+ -> Request Report s AlertTotals
150+alertsPerTime s u =
151+ mk Report
152+ { _rSince' = D s
153+ , _rUntil' = D u
154+ , _rRollup' = Monthly
155+ } & path .~ reports % "alerts_per_time"
156+
157+data IncidentReport = IncidentReport
158+ { _irStart :: Date
159+ , _irEnd :: Date
160+ , _irNumberOfIncidents :: !Int
161+ } deriving (Eq, Show)
162+
163+deriveJSON ''IncidentReport
164+
165+irStart :: Lens' IncidentReport UTCTime
166+irStart = lens _irStart (\r x -> r { _irStart = x }) . _D
167+
168+irEnd :: Lens' IncidentReport UTCTime
169+irEnd = lens _irEnd (\r x -> r { _irEnd = x }) . _D
170+
171+makeLens "_irNumberOfIncidents" ''IncidentReport
172+
173+-- | Get high level statistics about the number of incidents created for the
174+-- desired time period, summed daily, weekly or monthly.
175+--
176+-- @GET \/reports\/incidents_per_time@
177+--
178+-- /See:/ <http://developer.pagerduty.com/documentation/rest/reports/incidents_per_time>
179+incidentsPerTime :: UTCTime -- ^ 'rSince'
180+ -> UTCTime -- ^ 'rUntil'
181+ -> Request Report s [IncidentReport]
182+incidentsPerTime s u =
183+ mk Report
184+ { _rSince' = D s
185+ , _rUntil' = D u
186+ , _rRollup' = Monthly
187+ } & path .~ reports % "incidents_per_time"
188+ & unwrap .~ key "incidents"
src/Network/PagerDuty/REST/Schedules.hsnew file+638
@@ -0,0 +1,638 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.Schedules
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | On call schedules determine the time periods that users are on-call. Only when
20+-- a user is on-call he is eligible to receive alerts from incidents.
21+--
22+-- This API allows users to manipulate on-call schedules.
23+--
24+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/overrides>
25+module Network.PagerDuty.REST.Schedules
26+ (
27+ -- * List Schedules
28+ ListSchedules
29+ , listSchedules
30+ , lsQuery
31+
32+ -- * Get Schedule
33+ , GetSchedule
34+ , getSchedule
35+ , gsSince
36+ , gsUntil
37+ , gsTimeZone
38+
39+ -- * Get Schedule Users
40+ , GetScheduleUsers
41+ , getScheduleUsers
42+ , gsuSince
43+ , gsuUntil
44+
45+ -- * Get Schedule Entries
46+ , GetScheduleEntries
47+ , getScheduleEntries
48+ , gseSince
49+ , gseUntil
50+ , gseOverflow
51+ , gseTimeZone
52+ , gseUserId
53+
54+ -- * Create Schedule
55+ , CreateSchedule
56+ , createSchedule
57+ , csName
58+ , csOverflow
59+ , csTimeZone
60+ , csScheduleLayers
61+
62+ -- * Update Schedule
63+ , UpdateSchedule
64+ , updateSchedule
65+ , usOverflow
66+ , usTimeZone
67+ , usScheduleLayers
68+
69+ -- * Preview Schedule
70+ , PreviewSchedule
71+ , previewSchedule
72+ , psSince
73+ , psUntil
74+ , psOverflow
75+ , psName
76+ , psTimeZone
77+ , psScheduleLayers
78+
79+ -- * Delete Schedule
80+ , deleteSchedule
81+
82+ -- * Types
83+ , RestrictionType (..)
84+ , Restriction
85+ , rStartTimeOfDay
86+ , rDurationSeconds
87+
88+ , Rotation
89+ , rMemberOrder
90+ , rUser
91+
92+ , ScheduleLayer
93+ , slName
94+ , slRenderedScheduleEntries
95+ , slRestrictionType
96+ , slRestrictions
97+ , slPriority
98+ , slStart
99+ , slEnd
100+ , slRenderedCoveragePercentage
101+ , slRotationTurnLengthSeconds
102+ , slRotationVirtualStart
103+ , slUsers
104+
105+ , HasScheduleInfo (..)
106+ , ScheduleInfo
107+ , Schedule
108+ , sScheduleLayers
109+ , sOverridesSubschedule
110+ , sFinalSchedule
111+ ) where
112+
113+import Control.Lens hiding ((.=))
114+import Data.Aeson
115+import Data.Text (Text)
116+import Data.Time
117+import Network.HTTP.Types
118+import Network.PagerDuty.REST.Users (User)
119+import Network.PagerDuty.Internal.TH
120+import Network.PagerDuty.Internal.Types
121+
122+default (Path)
123+
124+schedules :: Path
125+schedules = "schedules"
126+
127+data RestrictionType
128+ = Daily
129+ | Weekly
130+ deriving (Eq, Show)
131+
132+deriveNullary ''RestrictionType
133+
134+data Restriction = Restriction
135+ { _rStartTimeOfDay :: Date
136+ , _rDurationSeconds :: !Int
137+ } deriving (Eq, Show)
138+
139+deriveJSON ''Restriction
140+
141+rStartTimeOfDay :: Lens' Restriction UTCTime
142+rStartTimeOfDay =
143+ lens _rStartTimeOfDay (\r x -> r { _rStartTimeOfDay = x }) . _D
144+
145+makeLens "_rDurationSeconds" ''Restriction
146+
147+data Rotation = Rotation
148+ { _rMemberOrder :: !Int
149+ , _rUser :: User
150+ } deriving (Eq, Show)
151+
152+deriveRecord ''Rotation
153+
154+-- | A schedule is composed of multiple schedule layers.
155+--
156+-- A layer is composed of a group of people who will rotate through the same
157+-- shift. In a basic weekly schedule, you create a single layer where each member
158+-- is on-call for one week, with a set day and time for transferring on-call duty.
159+--
160+-- When a schedule has multiple layers, the layer can be ordered using the
161+-- priority field. The layer with the highest priority has precedence over the
162+-- layers with lower priority. You can use restrictions to control how layers
163+-- overlap.
164+--
165+-- The override layer is a special layer where all the override entries are
166+-- stored.
167+--
168+-- The final layer is a special layer that contains the result of all the previous
169+-- layers put together. This layer cannot be edited.
170+data ScheduleLayer = ScheduleLayer
171+ { _slName :: Text
172+ , _slRenderedScheduleEntries :: [ScheduleId] -- FIXME: what is this a list of?
173+ , _slRestrictionType :: !RestrictionType
174+ , _slRestrictions :: [Restriction]
175+ , _slPriority :: !Int
176+ , _slStart :: Date
177+ , _slEnd :: Date
178+ , _slRenderedCoveragePercentage :: !Double
179+ , _slRotationTurnLengthSeconds :: !Int
180+ , _slRotationVirtualStart :: Date
181+ , _slUsers :: [Rotation]
182+ } deriving (Eq, Show)
183+
184+-- FIXME: most of these fields are maybes
185+
186+deriveJSON ''ScheduleLayer
187+
188+makeLens "_slName" ''ScheduleLayer
189+
190+-- | This is a list of entries to be rendered for the current time range.
191+makeLens "_slRenderedScheduleEntries" ''ScheduleLayer
192+
193+-- | This specifies the type of restrictions present on this layer.
194+makeLens "_slRestrictionType" ''ScheduleLayer
195+
196+-- | A list of time restrictions for this layer.
197+makeLens "_slRestrictions" ''ScheduleLayer
198+
199+-- | The priority of the layer. Layers with higher priority will override
200+-- layers with a lower priority.
201+makeLens "_slPriority" ''ScheduleLayer
202+
203+-- | The effective start date of the layer
204+makeLens "_slStart" ''ScheduleLayer
205+
206+-- | The effective end date of the layer. If 'Nothing', the layer has no end date.
207+makeLens "_slEnd" ''ScheduleLayer
208+
209+-- | The percentage of the time range covered by this layer.
210+makeLens "_slRenderedCoveragePercentage" ''ScheduleLayer
211+
212+-- | The duration of each on-call shift in seconds.
213+makeLens "_slRotationTurnLengthSeconds" ''ScheduleLayer
214+
215+-- | The effective start time of the layer. This can be before the start
216+-- time of the schedule.
217+makeLens "_slRotationVirtualStart" ''ScheduleLayer
218+
219+-- | An ordered list of users in this layer. The member_order field controls
220+-- the order in which the users apear in the rotation.
221+makeLens "_slUsers" ''ScheduleLayer
222+
223+data ScheduleInfo = ScheduleInfo
224+ { _siId' :: ScheduleId
225+ , _siName' :: Text
226+ , _siTimeZone' :: TZ
227+ , _siToday' :: Date
228+ , _siEscalationPolicies' :: [EscalationPolicyId]
229+ } deriving (Eq, Show)
230+
231+deriveRecord ''ScheduleInfo
232+
233+class HasScheduleInfo a where
234+ scheduleInfo :: Lens' a ScheduleInfo
235+
236+ -- | The unique identifier of the schedule.
237+ sId :: Lens' a ScheduleId
238+ -- | The name of the schedule.
239+ sName :: Lens' a Text
240+ -- | The time zone of the schedule.
241+ sTimeZone :: Lens' a TimeZone
242+ -- | The current day in the schedule's time zone.
243+ sToday :: Lens' a UTCTime
244+ -- | An list of all the escalation policies that uses this schedule.
245+ sEscalationPolicies :: Lens' a [EscalationPolicyId]
246+
247+ sId = scheduleInfo.siId'
248+ sName = scheduleInfo.siName'
249+ sTimeZone = scheduleInfo.siTimeZone'._TZ
250+ sToday = scheduleInfo.siToday'._D
251+ sEscalationPolicies = scheduleInfo.siEscalationPolicies'
252+
253+instance HasScheduleInfo ScheduleInfo where
254+ scheduleInfo = id
255+
256+data Schedule = Schedule
257+ { _sInfo :: ScheduleInfo
258+ , _sScheduleLayers :: [ScheduleLayer]
259+ , _sOverridesSubschedule :: ScheduleLayer
260+ , _sFinalSchedule :: ScheduleLayer
261+ } deriving (Eq, Show)
262+
263+deriveJSON ''Schedule
264+
265+instance HasScheduleInfo Schedule where
266+ scheduleInfo = lens _sInfo (\s x -> s { _sInfo = x })
267+
268+-- | A list of schedule layers. See the schedule layers parameters for details.
269+makeLens "_sScheduleLayers" ''Schedule
270+
271+-- | The schedule layer object where all the overrides are stored.
272+makeLens "_sOverridesSubschedule" ''Schedule
273+
274+-- | The final schedule layer object.
275+makeLens "_sFinalSchedule" ''Schedule
276+
277+newtype ListSchedules = ListSchedules
278+ { _lsQuery' :: Maybe Text
279+ } deriving (Eq, Show)
280+
281+queryRequest ''ListSchedules
282+
283+-- | Filters the result, showing only the schedules whose name matches the query.
284+lsQuery :: Lens' (Request ListSchedules s b) (Maybe Text)
285+lsQuery = upd.lsQuery'
286+
287+instance Paginate ListSchedules
288+
289+-- | List existing on-call schedules.
290+--
291+-- @GET \/schedules@
292+--
293+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/list>
294+listSchedules :: RequesterId -> Request ListSchedules s [ScheduleInfo]
295+listSchedules r = auth listSchedulesBasic & query .~ [("requester_id", r)]
296+
297+-- | A version of 'listSchedules' which uses HTTP Basic authentication and
298+-- doesn't require a 'RequesterId'.
299+listSchedulesBasic :: Request ListSchedules s [ScheduleInfo]
300+listSchedulesBasic =
301+ mk ListSchedules
302+ { _lsQuery' = Nothing
303+ } & path .~ schedules
304+
305+data GetSchedule = GetSchedule
306+ { _gsSince' :: Maybe Date
307+ , _gsUntil' :: Maybe Date
308+ , _gsTimeZone' :: Maybe TZ
309+ } deriving (Eq, Show)
310+
311+queryRequest ''GetSchedule
312+
313+-- | The start of the date range over which you want to return on-call schedule
314+-- entries and on-call schedule layers.
315+gsSince :: Lens' (Request GetSchedule s b) (Maybe UTCTime)
316+gsSince = upd.gsSince'.mapping _D
317+
318+-- | The end of the date range over which you want to return schedule entries
319+-- and on-call schedule layers.
320+gsUntil :: Lens' (Request GetSchedule s b) (Maybe UTCTime)
321+gsUntil = upd.gsUntil'.mapping _D
322+
323+-- | Time zone in which dates in the result will be rendered.
324+--
325+-- /Default:/ account time zone.
326+gsTimeZone :: Lens' (Request GetSchedule s b) (Maybe TimeZone)
327+gsTimeZone = upd.gsTimeZone'.mapping _TZ
328+
329+-- | Show detailed information about a schedule, including entries for each
330+-- layer and sub-schedule.
331+--
332+-- @GET \/schedules\/\:id@
333+--
334+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/show>
335+getSchedule :: Request GetSchedule s Schedule
336+getSchedule =
337+ mk GetSchedule
338+ { _gsSince' = Nothing
339+ , _gsUntil' = Nothing
340+ , _gsTimeZone' = Nothing
341+ } & path .~ schedules
342+
343+
344+data GetScheduleUsers = GetScheduleUsers
345+ { _gsuSince' :: Maybe Date
346+ , _gsuUntil' :: Maybe Date
347+ } deriving (Eq, Show)
348+
349+queryRequest ''GetScheduleUsers
350+
351+-- | The start of the date range over which you want to return on-call users.
352+gsuSince :: Lens' (Request GetScheduleUsers s b) (Maybe UTCTime)
353+gsuSince = upd.gsuSince'.mapping _D
354+
355+-- | The end time of the date range over which you want to return on-call users.
356+gsuUntil :: Lens' (Request GetScheduleUsers s b) (Maybe UTCTime)
357+gsuUntil = upd.gsuUntil'.mapping _D
358+
359+-- | List all the users on-call in a given schedule for a given time range.
360+--
361+-- @GET \/schedules\/\:id\/users@
362+--
363+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/users>
364+getScheduleUsers :: UserId -> Request GetScheduleUsers s [User]
365+getScheduleUsers u =
366+ mk GetScheduleUsers
367+ { _gsuSince' = Nothing
368+ , _gsuUntil' = Nothing
369+ } & path .~ schedules % u % "users"
370+
371+data GetScheduleEntries = GetScheduleEntries
372+ { _gseSince' :: Date
373+ , _gseUntil' :: Date
374+ , _gseOverflow' :: !Bool'
375+ , _gseTimeZone' :: Maybe TZ
376+ , _gseUserId' :: Maybe UserId
377+ } deriving (Eq, Show)
378+
379+queryRequest ''GetScheduleEntries
380+
381+-- | The start of the date range over which you want to return on-call
382+-- schedule entries.
383+-- The maximum range queryable at once is three months.
384+gseSince :: Lens' (Request GetScheduleEntries s b) UTCTime
385+gseSince = upd.gseSince'._D
386+
387+-- | The end of the date range over which you want to return schedule entries.
388+gseUntil :: Lens' (Request GetScheduleEntries s b) UTCTime
389+gseUntil = upd.gseUntil'._D
390+
391+-- | Any on-call schedule entries that pass the date range bounds will be
392+-- truncated at the bounds, unless the parameter overflow=true is passed.
393+--
394+-- Default: false.
395+--
396+-- Example: if your schedule is a rotation that changes daily at midnight UTC,
397+-- and your date range is from 2011-06-01T10:00:00Z to 2011-06-01T14:00:00Z: If
398+-- you don't pass the overflow=true parameter, you will get one schedule entry
399+-- returned with a start of 2011-06-01T10:00:00Z and end of 2011-06-01T14:00:00Z.
400+-- If you do pass the overflow=true parameter, you will get one schedule entry
401+-- returned with a start of 2011-06-01T00:00:00Z and end of 2011-06-02T00:00:00Z.
402+gseOverflow :: Lens' (Request GetScheduleEntries s b) Bool
403+gseOverflow = upd.gseOverflow'._B
404+
405+-- | Time zone in which dates in the result will be rendered.
406+--
407+-- Default: account time zone.
408+gseTimeZone :: Lens' (Request GetScheduleEntries s b) (Maybe TimeZone)
409+gseTimeZone = upd.gseTimeZone'.mapping _TZ
410+
411+-- | To filter the returned on-call schedule entries by a specific user, you
412+-- can optionally add the user_id parameter to the query.
413+gseUserId :: Lens' (Request GetScheduleEntries s b) (Maybe UserId)
414+gseUserId = upd.gseUserId'
415+
416+-- | List schedule entries that are active for a given time range for a
417+-- specified on-call schedule.
418+--
419+-- @GET \/schedules\/\:id\/entries@
420+--
421+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/entries>
422+getScheduleEntries :: UserId
423+ -> Date -- ^ 'gseSince'
424+ -> Date -- ^ 'gseUntil'
425+ -> Request GetScheduleEntries s b
426+getScheduleEntries i s u =
427+ mk GetScheduleEntries
428+ { _gseSince' = s
429+ , _gseUntil' = u
430+ , _gseOverflow' = F
431+ , _gseTimeZone' = Nothing
432+ , _gseUserId' = Nothing
433+ } & path .~ schedules % i
434+
435+data CreateSchedule = CreateSchedule
436+ { _csName' :: Text
437+ , _csOverflow' :: !Bool'
438+ , _csTimeZone' :: TZ
439+ , _csScheduleLayers' :: [ScheduleLayer]
440+ } deriving (Eq, Show)
441+
442+makeLenses ''CreateSchedule
443+
444+instance ToJSON CreateSchedule where
445+ toJSON cs = object
446+ [ "name" .= _csName' cs
447+ , "overflow" .= _csOverflow' cs
448+ , "schedule" .= object
449+ [ "time_zone" .= _csTimeZone' cs
450+ , "schedule_layers" .= _csScheduleLayers' cs
451+ ]
452+ ]
453+
454+instance QueryLike CreateSchedule where
455+ toQuery = const []
456+
457+-- | Any on-call schedule entries that pass the date range bounds will be
458+-- truncated at the bounds, unless 'csOverflow' is set to 'True'.
459+--
460+-- Default: 'False'.
461+csOverflow :: Lens' (Request CreateSchedule s b) Bool
462+csOverflow = upd.csOverflow'._B
463+
464+-- | The name of the schedule.
465+csName :: Lens' (Request CreateSchedule s b) Text
466+csName = upd.csName'
467+
468+-- | The time zone of the schedule.
469+--
470+-- Default: 'UTC'.
471+csTimeZone :: Lens' (Request CreateSchedule s b) TimeZone
472+csTimeZone = upd.csTimeZone'._TZ
473+
474+-- | A list of schedule layers.
475+csScheduleLayers :: Lens' (Request CreateSchedule s b) [ScheduleLayer]
476+csScheduleLayers = upd.csScheduleLayers'
477+
478+-- | Create a new on-call schedule.
479+--
480+-- @POST \/schedules@
481+--
482+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/create>
483+createSchedule :: Text -- ^ 'csName'
484+ -> TimeZone -- ^ 'csTimeZone'
485+ -> [ScheduleLayer] -- ^ 'csScheduleLayers'
486+ -> Request CreateSchedule s Schedule
487+createSchedule n z ls =
488+ mk CreateSchedule
489+ { _csName' = n
490+ , _csOverflow' = F
491+ , _csTimeZone' = TZ z
492+ , _csScheduleLayers' = ls
493+ } & meth .~ POST
494+ & path .~ schedules
495+
496+data UpdateSchedule = UpdateSchedule
497+ { _usOverflow' :: !Bool'
498+ , _usTimeZone' :: TZ
499+ , _usScheduleLayers' :: [ScheduleLayer]
500+ } deriving (Eq, Show)
501+
502+makeLenses ''UpdateSchedule
503+
504+instance ToJSON UpdateSchedule where
505+ toJSON us = object
506+ [ "overflow" .= _usOverflow' us
507+ , "schedule" .= object
508+ [ "time_zone" .= _usTimeZone' us
509+ , "schedule_layers" .= _usScheduleLayers' us
510+ ]
511+ ]
512+
513+instance QueryLike UpdateSchedule where
514+ toQuery = const []
515+
516+-- | Any on-call schedule entries that pass the date range bounds will be
517+-- truncated at the bounds, unless 'usOverflow' is set to 'True'.
518+--
519+-- Default: 'False'.
520+usOverflow :: Lens' (Request UpdateSchedule s b) Bool
521+usOverflow = upd.usOverflow'._B
522+
523+-- | The time zone of the schedule.
524+usTimeZone :: Lens' (Request UpdateSchedule s b) TimeZone
525+usTimeZone = upd.usTimeZone'._TZ
526+
527+-- | A list of schedule layers.
528+usScheduleLayers :: Lens' (Request UpdateSchedule s b) [ScheduleLayer]
529+usScheduleLayers = upd.usScheduleLayers'
530+
531+-- | Update an existing on-call schedule.
532+--
533+-- /Note:/ You cannot delete schedule layers. You must include all layers in
534+-- your update request. To delete a layer, set the end parameter to schedule its
535+-- termination.
536+--
537+-- @PUT \/schedules\/\:id@
538+--
539+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/update>
540+updateSchedule :: ScheduleId
541+ -> TimeZone -- ^ 'usTimeZone'
542+ -> [ScheduleLayer] -- ^ 'usScheduleLayers'
543+ -> Request UpdateSchedule s Schedule
544+updateSchedule s z ls =
545+ mk UpdateSchedule
546+ { _usOverflow' = F
547+ , _usTimeZone' = TZ z
548+ , _usScheduleLayers' = ls
549+ } & meth .~ PUT
550+ & path .~ schedules % s
551+
552+data PreviewSchedule = PreviewSchedule
553+ { _psSince' :: Maybe Date
554+ , _psUntil' :: Maybe Date
555+ , _psName' :: Text
556+ , _psOverflow' :: !Bool'
557+ , _psTimeZone' :: TZ
558+ , _psScheduleLayers' :: [ScheduleLayer]
559+ } deriving (Eq, Show)
560+
561+makeLenses ''PreviewSchedule
562+
563+instance ToJSON PreviewSchedule where
564+ toJSON ps = object
565+ [ "since" .= _psSince' ps
566+ , "until" .= _psUntil' ps
567+ , "name" .= _psName' ps
568+ , "overflow" .= _psOverflow' ps
569+ , "schedule" .= object
570+ [ "time_zone" .= _psTimeZone' ps
571+ , "schedule_layers" .= _psScheduleLayers' ps
572+ ]
573+ ]
574+
575+instance QueryLike PreviewSchedule where
576+ toQuery = const []
577+
578+-- | The start of the date range over which you want to return on-call schedule
579+-- entries and on-call schedule layers.
580+psSince :: Lens' (Request PreviewSchedule s b) (Maybe UTCTime)
581+psSince = upd.psSince'.mapping _D
582+
583+-- | The end of the date range over which you want to return schedule entries
584+-- and on-call schedule layers.
585+psUntil :: Lens' (Request PreviewSchedule s b) (Maybe UTCTime)
586+psUntil = upd.psUntil'.mapping _D
587+
588+-- | Any on-call schedule entries that pass the date range bounds will be
589+-- truncated at the bounds, unless 'psOverflow' is set to 'True'.
590+--
591+-- Default: 'False'.
592+psOverflow :: Lens' (Request PreviewSchedule s b) Bool
593+psOverflow = upd.psOverflow'._B
594+
595+-- | The name of the schedule.
596+psName :: Lens' (Request PreviewSchedule s b) Text
597+psName = upd.psName'
598+
599+-- | The time zone of the schedule.
600+--
601+-- Default: 'UTC'.
602+psTimeZone :: Lens' (Request PreviewSchedule s b) TimeZone
603+psTimeZone = upd.psTimeZone'._TZ
604+
605+-- | A list of schedule layers.
606+psScheduleLayers :: Lens' (Request PreviewSchedule s b) [ScheduleLayer]
607+psScheduleLayers = upd.psScheduleLayers'
608+
609+-- | Preview what a schedule would look like without saving it. This work the
610+-- same as the update or create actions, except that the result is not
611+-- persisted. Preview optionally takes two additional arguments, since and until,
612+-- deliminating the span of the preview.
613+--
614+-- @POST \/schedules\/preview@
615+--
616+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/preview>
617+previewSchedule :: Text -- ^ 'psName'
618+ -> TimeZone -- ^ 'psTimeZone'
619+ -> [ScheduleLayer] -- ^ 'psScheduleLayers'
620+ -> Request PreviewSchedule s Schedule
621+previewSchedule n z ls =
622+ mk PreviewSchedule
623+ { _psSince' = Nothing
624+ , _psUntil' = Nothing
625+ , _psName' = n
626+ , _psOverflow' = F
627+ , _psTimeZone' = TZ z
628+ , _psScheduleLayers' = ls
629+ } & meth .~ POST
630+ & path .~ schedules % "preview"
631+
632+-- | Delete an on-call schedule.
633+--
634+-- @DELETE \/schedules\/\:id@
635+--
636+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/delete>
637+deleteSchedule :: ScheduleId -> Request Empty s Empty
638+deleteSchedule s = empty & meth .~ DELETE & path .~ schedules % s
src/Network/PagerDuty/REST/Schedules/Overrides.hsnew file+188
@@ -0,0 +1,188 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.Schedules
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | Schedule overrides are custom, non-recurring exceptions to your regular
20+-- on-call schedules. Use them when your team members go on vacation, swap shifts,
21+-- or when you simply cannot achieve your normal scheduling with recurring layers.
22+--
23+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules>
24+module Network.PagerDuty.REST.Schedules.Overrides
25+ (
26+ -- * List Overrides
27+ ListOverrides
28+ , listOverrides
29+ , lsSince
30+ , lsUntil
31+ , lsEditable
32+ , lsOverflow
33+
34+ -- * Create Override
35+ , CreateOverride
36+ , createOverride
37+ , coUserId
38+ , coStart
39+ , coEnd
40+
41+ -- * Delete Override
42+ , deleteOverride
43+
44+ -- * Types
45+ , HasUserInfo (..)
46+ , Override
47+ , oId
48+ , oStart
49+ , oEnd
50+ , oUser
51+ ) where
52+
53+import Control.Lens
54+import Data.Time
55+import Network.HTTP.Types
56+import Network.PagerDuty.REST.Users
57+import Network.PagerDuty.Internal.TH
58+import Network.PagerDuty.Internal.Types
59+
60+overrides :: ScheduleId -> Path
61+overrides i = "schedules" % i % "overrides"
62+
63+data Override = Override
64+ { _oId :: Maybe OverrideId
65+ , _oStart :: Date
66+ , _oEnd :: Date
67+ , _oUser :: UserInfo
68+ } deriving (Eq, Show)
69+
70+deriveRecord ''Override
71+
72+instance HasUserInfo Override where
73+ userInfo = oUser
74+
75+data ListOverrides = ListOverrides
76+ { _lsSince' :: Date
77+ , _lsUntil' :: Date
78+ , _lsEditable' :: Bool'
79+ , _lsOverflow' :: Bool'
80+ } deriving (Eq, Show)
81+
82+queryRequest ''ListOverrides
83+
84+-- | The start time of the date range you want to retrieve override for.
85+--
86+-- The maximum date range queryable is 3 months.
87+lsSince :: Lens' (Request ListOverrides s b) UTCTime
88+lsSince = upd.lsSince'._D
89+
90+-- | The end time of the date range you want to retrieve override for.
91+lsUntil :: Lens' (Request ListOverrides s b) UTCTime
92+lsUntil = upd.lsUntil'._D
93+
94+-- | When 'True', only editable overrides will be returned.
95+--
96+-- The result will onlyinclude the id the override if this parameter is present.
97+-- Only future overrides are editable.
98+--
99+-- /Default:/ 'False.'
100+lsEditable :: Lens' (Request ListOverrides s b) Bool
101+lsEditable = upd.lsEditable'._B
102+
103+-- | Any on-call schedule entries that pass the date range bounds will be
104+-- truncated at the bounds, unless @lsEditable .~ True@ is specified.
105+--
106+-- /Default:/ 'False.'
107+lsOverflow :: Lens' (Request ListOverrides s b) Bool
108+lsOverflow = upd.lsOverflow'._B
109+
110+-- | List overrides for a given time range.
111+--
112+-- @GET \/schedules\/\:schedule_id\/overrides@
113+--
114+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/overrides/list>
115+listOverrides :: ScheduleId
116+ -> Date -- ^ 'lsSince'
117+ -> Date -- ^ 'lsUntil'
118+ -> Request ListOverrides s [Override]
119+listOverrides i s u =
120+ mk ListOverrides
121+ { _lsSince' = s
122+ , _lsUntil' = u
123+ , _lsEditable' = F
124+ , _lsOverflow' = F
125+ } & path .~ overrides i
126+
127+data CreateOverride = CreateOverride
128+ { _coStart' :: Date
129+ , _coEnd' :: Date
130+ , _coUserId' :: UserId
131+ } deriving (Eq, Show)
132+
133+jsonRequest ''CreateOverride
134+
135+-- | The start date and time for the override.
136+coStart :: Lens' (Request CreateOverride s b) UTCTime
137+coStart = upd.coStart'._D
138+
139+-- | The end date and time for the override.
140+coEnd :: Lens' (Request CreateOverride s b) UTCTime
141+coEnd = upd.coEnd'._D
142+
143+-- | The ID of the user who will be on call for the duration of the override.
144+coUserId :: Lens' (Request CreateOverride s b) UserId
145+coUserId = upd.coUserId'
146+
147+-- | Create an override for a specific user covering the specified time range. If
148+-- you create an override on top of an existing one, the last created override
149+-- will have priority.
150+--
151+-- If no time zone information is present in the since and until parameters, the
152+-- schedule's time zone will be used.
153+--
154+-- /Note:/ You cannot create overrides in the past.
155+--
156+-- @POST \/schedules\/\:schedule_id\/overrides@
157+--
158+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/overrides/create>
159+createOverride :: ScheduleId
160+ -> UserId -- ^ 'coUserId'
161+ -> Date -- ^ 'coStart'
162+ -> Date -- ^ 'coEnd'
163+ -> Request CreateOverride s Override
164+createOverride i u s e =
165+ mk CreateOverride
166+ { _coUserId' = u
167+ , _coStart' = s
168+ , _coEnd' = e
169+ } & meth .~ POST
170+ & path .~ overrides i
171+
172+-- | Remove an override.
173+--
174+-- You cannot remove a past override. If the override start
175+-- time is before the current time, but the end time is after the current time,
176+-- the override will be truncated to the current time. If the override is
177+-- truncated, the status code will be 200 OK, as opposed to a 204 No Content for a
178+-- successful delete.
179+--
180+-- @DELETE \/schedules\/\:schedule_id\/overrides\/\:id@
181+--
182+-- /See:/ <http://developer.pagerduty.com/documentation/rest/schedules/overrides/delete>
183+
184+-- FIXME: How to parse whether truncated or deleted?
185+deleteOverride :: ScheduleId -> OverrideId -> Request Empty s Empty
186+deleteOverride i o = empty
187+ & meth .~ DELETE
188+ & path .~ overrides i % o
src/Network/PagerDuty/REST/Services.hsnew file+513
@@ -0,0 +1,513 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.Services
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | This API lets you access and manipulate the services across your account.
20+--
21+-- A service is an endpoint (like Nagios, email, or an API call) that
22+-- generates events, which Pagerduty normalizes and dedupes, creating incidents.
23+--
24+-- When a service is shown inlined in other resources, a deleted service will
25+-- have its @html_url@ attribute set to 'Nothing'.
26+--
27+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services>
28+module Network.PagerDuty.REST.Services
29+ (
30+ -- * List Services
31+ ListServices
32+ , listServices
33+ , lsTimeZone
34+
35+ -- * Create Service
36+ , CreateService
37+ , createService
38+ , csName
39+ , csDescription
40+ , csType
41+ , csVendorId
42+ , csEscalationPolicyId
43+ , csAcknowledgementTimeout
44+ , csAutoResolveTimeout
45+ , csSeverityFilter
46+
47+ -- * Get Service
48+ , getService
49+
50+ -- * Update Service
51+ , UpdateService
52+ , updateService
53+ , usName
54+ , usEscalationPolicyId
55+ , usDescription
56+ , usAcknowledgementTimeout
57+ , usAutoResolveTimeout
58+ , usSeverityFilter
59+
60+ -- * Delete Service
61+ , deleteService
62+
63+ -- * Enable Service
64+ , enableService
65+
66+ -- * Disable Service
67+ , disableService
68+
69+ -- * Regenerate Key
70+ , regenerateKey
71+
72+ -- * Types
73+ , EmailFilterMode (..)
74+ , EmailIncidentCreation (..)
75+ , ServiceStatus (..)
76+ , ServiceType (..)
77+ , SeverityFilter (..)
78+
79+ , IncidentCounts
80+ , cntTriggered
81+ , cntAcknowledged
82+ , cntResolved
83+ , cntTotal
84+
85+ , PolicyInfo
86+ , pinfoId
87+ , pinfoName
88+
89+ , Service
90+ , svcId
91+ , svcName
92+ , svcDescription
93+ , svcServiceUrl
94+ , svcServiceKey
95+ , svcAutoResolveTimeout
96+ , svcAcknowledgementTimeout
97+ , svcCreatedAt
98+ , svcStatus
99+ , svcLastIncidentTimestamp
100+ , svcEmailIncidentCreation
101+ , svcIncidentCounts
102+ , svcEmailFilterMode
103+ , svcType
104+ , svcEscalationPolicy
105+ , svcEmailFilters
106+ , svcSeverityFilter
107+ ) where
108+
109+import Control.Lens
110+import Data.Aeson.Lens
111+import Data.Text (Text)
112+import Data.Time
113+import Network.HTTP.Types
114+import Network.PagerDuty.REST.Services.EmailFilters
115+import Network.PagerDuty.Internal.TH
116+import Network.PagerDuty.Internal.Types
117+
118+default (Path)
119+
120+services :: Path
121+services = "services"
122+
123+includes :: Query
124+includes =
125+ [ ("include[]", Just "escalation_policy")
126+ , ("include[]", Just "email_filters")
127+ ]
128+
129+data IncidentCounts = IncidentCounts
130+ { _cntTriggered :: !Int
131+ , _cntAcknowledged :: !Int
132+ , _cntResolved :: !Int
133+ , _cntTotal :: !Int
134+ } deriving (Eq, Show)
135+
136+deriveRecord ''IncidentCounts
137+
138+data EmailIncidentCreation
139+ = OnNewEmail
140+ -- ^ Open a new incident for each trigger email.
141+ | OnNewEmailSubject
142+ -- ^ Open a new incident for each new trigger email subject.
143+ | OnlyIfNoOpenIncidents
144+ -- ^ Open a new incident only if an open incident does not already exist.
145+ deriving (Eq, Show)
146+
147+deriveNullary ''EmailIncidentCreation
148+
149+data EmailFilterMode
150+ = AllEmail
151+ -- ^ Accept all incoming email
152+ | OrRulesEmail
153+ -- ^ Accept email only if it matches ONE OR MORE rules below
154+ | AndRulesEmail
155+ -- ^ Accept email only if it matches ALL of the rules below
156+ deriving (Eq, Show)
157+
158+deriveNullaryWith hyphenated ''EmailFilterMode
159+
160+data ServiceStatus
161+ = Active
162+ -- ^ The service is enabled and has no open incidents.
163+ | Warning
164+ -- ^ The service is enabled and has one or more acknowledged incidents.
165+ | Critical
166+ -- ^ The service is enabled and has one or more triggered incidents.
167+ | Maintenance
168+ -- ^ The service is under maintenance, no new incidents will be
169+ -- triggered during maintenance mode.
170+ | Disabled
171+ -- ^ The service is disabled and will not have any new triggered incidents.
172+ deriving (Eq, Show)
173+
174+deriveNullary ''ServiceStatus
175+
176+data ServiceType
177+ = CloudKick
178+ | GenericEmail
179+ | GenericEventsApi
180+ | Keynote
181+ | Nagios
182+ | Pingdom
183+ | ServerDensity
184+ | SqlMonitor
185+ deriving (Eq, Show)
186+
187+deriveNullary ''ServiceType
188+
189+data PolicyInfo = PolicyInfo
190+ { _pinfoId :: EscalationPolicyId
191+ , _pinfoName :: Text
192+ } deriving (Eq, Show)
193+
194+deriveRecord ''PolicyInfo
195+
196+data SeverityFilter
197+ = SevCritical
198+ -- ^ Incidents are created when an alarm enters the Critical state.
199+ | SevCriticalOrWarning
200+ -- ^ Incidents are created when an alarm enters the Critical OR Warning states
201+ | SevOnAny
202+ -- ^ SQL Monitor: Incidents are created for alerts of any severity.
203+ | SevOnHigh
204+ -- ^ SQL Monitor: Incidents are created for alerts with high severity.
205+ | SevOnMediumHigh
206+ -- ^ SQL Monitor: Incidents are created for with high or medium severity
207+ deriving (Eq, Show)
208+
209+deriveNullaryWith (dropped 3 underscored) ''SeverityFilter
210+
211+data Service = Service
212+ { _svcId :: ServiceId
213+ , _svcName :: Text
214+ , _svcDescription :: Text
215+ , _svcServiceUrl :: Text
216+ , _svcServiceKey :: ServiceKey
217+ , _svcAutoResolveTimeout :: Maybe Int
218+ , _svcAcknowledgementTimeout :: Maybe Int
219+ , _svcCreatedAt :: Date
220+ , _svcStatus :: ServiceStatus
221+ , _svcLastIncidentTimestamp :: Maybe Date
222+ , _svcEmailIncidentCreation :: Maybe EmailIncidentCreation
223+ , _svcIncidentCounts :: IncidentCounts
224+ , _svcEmailFilterMode :: EmailFilterMode
225+ , _svcType :: ServiceType
226+ , _svcEscalationPolicy :: Maybe PolicyInfo -- FIXME: extract from inline.
227+ , _svcEmailFilters :: Maybe [EmailFilterInfo] -- FIXME: extract from inline.
228+ , _svcSeverityFilter :: Maybe SeverityFilter
229+ } deriving (Eq, Show)
230+
231+deriveJSON ''Service
232+
233+-- | A unique identifier for this service.
234+makeLens "_svcId" ''Service
235+
236+-- | The name of the service.
237+makeLens "_svcName" ''Service
238+
239+-- | The description of the service
240+makeLens "_svcDescription" ''Service
241+
242+-- | Relative URL that corresponds to this service.
243+makeLens "_svcServiceUrl" ''Service
244+
245+-- | For Email services, the serviceKey is the associated email address
246+-- for the service.
247+-- For all other service types, this is the unique key used for API calls.
248+makeLens "_svcServiceKey" ''Service
249+
250+-- | Time in seconds that an incident changes to the Triggered State after
251+-- being Acknowledged. Value is 'Nothing' if the feature is disabled.
252+makeLens "_svcAutoResolveTimeout" ''Service
253+
254+-- | Time in seconds that an incident is automatically resolved if left open for
255+-- that long. Value is 'Nothing' is the feature is disabled.
256+makeLens "_svcAcknowledgementTimeout" ''Service
257+
258+-- | The date/time when this service was created.
259+svcCreatedAt :: Lens' Service UTCTime
260+svcCreatedAt = lens _svcCreatedAt (\s x -> s { _svcCreatedAt = x }) . _D
261+
262+-- | The current state of the Service.
263+makeLens "_svcStatus" ''Service
264+
265+-- | The date/time when the most recent incident was created for this service.
266+svcLastIncidentTimestamp :: Lens' Service (Maybe UTCTime)
267+svcLastIncidentTimestamp =
268+ lens _svcLastIncidentTimestamp
269+ (\s x -> s { _svcLastIncidentTimestamp = x }) . mapping _D
270+
271+-- | If the service is a Generic Email service, this describes what kind of
272+-- emails create an incident.
273+makeLens "_svcEmailIncidentCreation" ''Service
274+
275+-- | An object with the number of incidents corresponding to these states.
276+makeLens "_svcIncidentCounts" ''Service
277+
278+-- | If the service is a Generic Email service, this describes which types of
279+-- email will generate an incident.
280+makeLens "_svcEmailFilterMode" ''Service
281+
282+-- | The service type.
283+makeLens "_svcType" ''Service
284+
285+-- | An object containing the ID and name of the escalation policy used by this
286+-- service.
287+makeLens "_svcEscalationPolicy" ''Service
288+
289+-- | An object containing inline Email Filters.
290+-- Note that only genericEmail services have filters.
291+makeLens "_svcEmailFilters" ''Service
292+
293+-- | Specifies what severity levels will create a new open incident.
294+makeLens "_svcSeverityFilter" ''Service
295+
296+newtype ListServices = ListServices
297+ { _lsTimeZone' :: Maybe TZ
298+ } deriving (Eq, Show)
299+
300+instance Paginate ListServices
301+
302+queryRequest ''ListServices
303+
304+-- | Time zone in which dates in the result will be rendered.
305+--
306+-- /Default:/ account default time zone.
307+lsTimeZone :: Lens' (Request ListServices s r) (Maybe TimeZone)
308+lsTimeZone = upd.lsTimeZone'.mapping _TZ
309+
310+-- | List existing services.
311+--
312+-- @GET \/services@
313+--
314+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/list>
315+listServices :: Request ListServices s [Service]
316+listServices =
317+ mk ListServices
318+ { _lsTimeZone' = Nothing
319+ } & path .~ services
320+ & query .~ includes
321+ & unwrap .~ key "escalation_policies"
322+
323+data CreateService = CreateService
324+ { _csName' :: Text
325+ , _csEscalationPolicyId' :: EscalationPolicyId
326+ , _csType' :: !ServiceType
327+ , _csVendorId' :: Maybe VendorId
328+ , _csDescription' :: Maybe Text
329+ , _csAcknowledgementTimeout' :: Maybe Int
330+ , _csAutoResolveTimeout' :: Maybe Int
331+ , _csSeverityFilter' :: Maybe SeverityFilter
332+ } deriving (Eq, Show)
333+
334+jsonRequest ''CreateService
335+
336+-- | The name of the service.
337+csName :: Lens' (Request CreateService s r) Text
338+csName = upd.csName'
339+
340+-- | A description for your service. 1024 character maximum.
341+csDescription :: Lens' (Request CreateService s r) (Maybe Text)
342+csDescription = upd.csDescription'
343+
344+-- | The type of service to create.
345+csType :: Lens' (Request CreateService s r) ServiceType
346+csType = upd.csType'
347+
348+-- | PagerDuty's internal vendor identifier for this service. For more information
349+-- about a specific vendor, please contact PagerDuty Support.
350+csVendorId :: Lens' (Request CreateService s r) (Maybe VendorId)
351+csVendorId = upd.csVendorId'
352+
353+-- | The id of the escalation policy to be used by this service.
354+csEscalationPolicyId :: Lens' (Request CreateService s r) EscalationPolicyId
355+csEscalationPolicyId = upd.csEscalationPolicyId'
356+
357+-- | The duration in seconds before an incidents acknowledged in this service
358+-- become triggered again.
359+--
360+-- /Default:/ 30 minutes.
361+csAcknowledgementTimeout :: Lens' (Request CreateService s r) (Maybe Int)
362+csAcknowledgementTimeout = upd.csAcknowledgementTimeout'
363+
364+-- | The duration in seconds before a triggered incident auto-resolves itself.
365+--
366+-- /Default:/ 4 hours.
367+csAutoResolveTimeout :: Lens' (Request CreateService s r) (Maybe Int)
368+csAutoResolveTimeout = upd.csAutoResolveTimeout'
369+
370+-- | Specifies what severity levels will create a new open incident.
371+csSeverityFilter :: Lens' (Request CreateService s r) (Maybe SeverityFilter)
372+csSeverityFilter = upd.csSeverityFilter'
373+
374+-- | Creates a new service.
375+--
376+-- @POST \/services@
377+--
378+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/create>
379+createService :: Text
380+ -> EscalationPolicyId
381+ -> ServiceType
382+ -> Request CreateService s Service
383+createService n p t =
384+ mk CreateService
385+ { _csName' = n
386+ , _csEscalationPolicyId' = p
387+ , _csType' = t
388+ , _csVendorId' = Nothing
389+ , _csDescription' = Nothing
390+ , _csAcknowledgementTimeout' = Nothing
391+ , _csAutoResolveTimeout' = Nothing
392+ , _csSeverityFilter' = Nothing
393+ } & path .~ services
394+ & unwrap .~ key "service"
395+
396+-- | Get details about an existing service.
397+--
398+-- @GET services\/\:id@
399+--
400+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/show>
401+getService :: ServiceId -> Request Empty s Service
402+getService i = empty
403+ & path .~ services % i
404+ & query <>~ includes
405+ & unwrap .~ key "service"
406+
407+data UpdateService = UpdateService
408+ { _usName' :: Maybe Text
409+ , _usDescription' :: Maybe Text
410+ , _usEscalationPolicyId' :: Maybe EscalationPolicyId
411+ , _usAcknowledgementTimeout' :: Maybe Int
412+ , _usAutoResolveTimeout' :: Maybe Int
413+ , _usSeverityFilter' :: Maybe SeverityFilter
414+ } deriving (Eq, Show)
415+
416+jsonRequest ''UpdateService
417+
418+-- | The name of the service.
419+usName :: Lens' (Request UpdateService s r) (Maybe Text)
420+usName = upd.usName'
421+
422+-- | A description for your service. 1024 character maximum.
423+usDescription :: Lens' (Request UpdateService s r) (Maybe Text)
424+usDescription = upd.usDescription'
425+
426+-- | The id of the escalation policy to be used by this service.
427+usEscalationPolicyId :: Lens' (Request UpdateService s r) (Maybe EscalationPolicyId)
428+usEscalationPolicyId = upd.usEscalationPolicyId'
429+
430+-- | The duration in seconds before an incidents acknowledged in this service
431+-- become triggered again.
432+--
433+-- /Default:/ 30 minutes.
434+usAcknowledgementTimeout :: Lens' (Request UpdateService s r) (Maybe Int)
435+usAcknowledgementTimeout = upd.usAcknowledgementTimeout'
436+
437+-- | The duration in seconds before a triggered incident auto-resolves itself.
438+--
439+-- /Default:/ 4 hours.
440+usAutoResolveTimeout :: Lens' (Request UpdateService s r) (Maybe Int)
441+usAutoResolveTimeout = upd.usAutoResolveTimeout'
442+
443+-- | Specifies what severity levels will create a new open incident.
444+usSeverityFilter :: Lens' (Request UpdateService s r) (Maybe SeverityFilter)
445+usSeverityFilter = upd.usSeverityFilter'
446+
447+-- | Update an existing service.
448+--
449+-- @PUT services\/\:id@
450+--
451+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/update>
452+updateService :: ServiceId -> Request UpdateService s Service
453+updateService i =
454+ mk UpdateService
455+ { _usName' = Nothing
456+ , _usEscalationPolicyId' = Nothing
457+ , _usDescription' = Nothing
458+ , _usAcknowledgementTimeout' = Nothing
459+ , _usAutoResolveTimeout' = Nothing
460+ , _usSeverityFilter' = Nothing
461+ } & meth .~ PUT
462+ & path .~ services % i
463+ & unwrap .~ key "service"
464+
465+-- | Delete an existing service. Once the service is deleted, it will not be
466+-- accessible from the web UI and new incidents won't be able to be created
467+-- for this service.
468+--
469+-- @DELETE \/services/:id@
470+--
471+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/delete>
472+deleteService :: ServiceId -> Request Empty s Empty
473+deleteService i = empty & meth .~ DELETE & path .~ services % i
474+
475+-- | Enable a previously disabled service.
476+--
477+-- @PUT services\/\:id\/enable@
478+--
479+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/enable>
480+enableService :: RequesterId -> ServiceId -> Request Empty s Empty
481+enableService r i =
482+ auth (enableServiceBasic i) & query .~ [("requester_id", r)]
483+
484+-- | A version of 'enableService' which uses HTTP Basic authentication and
485+-- doesn't require a 'RequesterId'.
486+enableServiceBasic :: ServiceId -> Request Empty Basic Empty
487+enableServiceBasic i = empty & meth .~ PUT & path .~ services % i % "enable"
488+
489+-- | Disable a service. Once a service is disabled, it will not be able to
490+-- create incidents until it is enabled again.
491+--
492+-- @PUT services\/\:id\/disable@
493+--
494+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/disable>
495+disableService :: RequesterId -> ServiceId -> Request Empty s Empty
496+disableService r i =
497+ auth (disableServiceBasic i) & query .~ [("requester_id", r)]
498+
499+-- | A version of 'disableService' which uses HTTP Basic authentication and
500+-- doesn't require a 'RequesterId'.
501+disableServiceBasic :: ServiceId -> Request Empty Basic Empty
502+disableServiceBasic i = empty & meth .~ PUT & path .~ services % i % "disable"
503+
504+-- | Regenerate a new service key for an existing service.
505+--
506+-- _Warning! The service's previous key will be invalidated, and existing
507+-- monitoring integrations will need to be modified to use the new key!_
508+--
509+-- @POST services\/\:id\/regenerate_key@
510+--
511+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/regenerate_key>
512+regenerateKey :: ServiceId -> Request Empty s Service
513+regenerateKey i = empty & meth .~ POST & path .~ services % i % "regenerate_key"
src/Network/PagerDuty/REST/Services/EmailFilters.hsnew file+180
@@ -0,0 +1,180 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
6+
7+-- Module : Network.PagerDuty.REST.Services.EmailFilters
8+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9+-- License : This Source Code Form is subject to the terms of
10+-- the Mozilla Public License, v. 2.0.
11+-- A copy of the MPL can be found in the LICENSE file or
12+-- you can obtain it at http://mozilla.org/MPL/2.0/.
13+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14+-- Stability : experimental
15+-- Portability : non-portable (GHC extensions)
16+
17+-- | Email Filters are a set of rules that are applied to triggering email's body,
18+-- subject and from address. It only applies to generic_email kind of
19+-- Services. The way multiple filters are combined depends on the
20+-- email_filter_mode attribute of the service.
21+--
22+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/email_filters>
23+module Network.PagerDuty.REST.Services.EmailFilters
24+ (
25+ -- * Create Email Filter
26+ createEmailFilter
27+
28+ -- * Update Email Filter
29+ , updateEmailFilter
30+
31+ -- * Delete Email Filter
32+ , deleteEmailFilter
33+
34+ -- * Types
35+ , MatchMode (..)
36+ , HasEmailFilterInfo (..)
37+ , EmailFilterInfo
38+ , EmailFilter
39+ , efId
40+ ) where
41+
42+import Control.Applicative hiding (empty)
43+import Control.Lens hiding ((.=))
44+import Data.Aeson
45+import Data.Default.Class
46+import Data.Monoid
47+import Data.Text (Text)
48+import Network.HTTP.Types
49+import Network.PagerDuty.Internal.TH
50+import Network.PagerDuty.Internal.Types
51+
52+default (Path)
53+
54+filters :: ServiceId -> Path
55+filters s = "services" % s % "email_filters"
56+
57+data MatchMode
58+ = Always
59+ | Match
60+ | NoMatch
61+ deriving (Eq, Show)
62+
63+deriveNullaryWith hyphenated ''MatchMode
64+
65+-- FIXME: Tighten up this type! Make the regex required for match/no-match and
66+-- encode the conditional invariants.
67+data EmailFilterInfo = EmailFilterInfo
68+ { _efSubjectMode' :: Maybe MatchMode
69+ , _efSubjectRegex' :: Maybe Text
70+ , _efBodyMode' :: Maybe MatchMode
71+ , _efBodyRegex' :: Maybe Text
72+ , _efFromEmailMode' :: Maybe MatchMode
73+ , _efFromEmailRegex' :: Maybe Text
74+ } deriving (Eq, Show)
75+
76+deriveRecord ''EmailFilterInfo
77+
78+instance Default EmailFilterInfo where
79+ def = EmailFilterInfo Nothing Nothing Nothing Nothing Nothing Nothing
80+
81+instance QueryLike EmailFilterInfo where
82+ toQuery = const []
83+
84+class HasEmailFilterInfo a where
85+ emailFilterInfo :: Lens' a EmailFilterInfo
86+
87+ -- | One of always, match, no-match, which, respectively, means to not
88+ -- filter the email trigger by subject, filter it if the email subject
89+ -- matches the given regex, or filter if it doesn't match the given regex.
90+ --
91+ -- /Default:/ always.
92+ efSubjectMode :: Lens' a (Maybe MatchMode)
93+
94+ -- | The regex to be used when subject_mode is match or no-match.
95+ -- It is a required parameter on such cases.
96+ efSubjectRegex :: Lens' a (Maybe Text)
97+
98+ -- | One of always, match, no-match, which, respectively, means to not filter
99+ -- the email trigger by body, filter it if the body email matches the given
100+ -- regex, or filter if it doesn't match the given regex.
101+ --
102+ -- /Default:/ always.
103+ efBodyMode :: Lens' a (Maybe MatchMode)
104+
105+ -- | The regex to be used when body_mode is match or no-match.
106+ -- It is a required parameter on such cases.
107+ efBodyRegex :: Lens' a (Maybe Text)
108+
109+ -- | One of always, match, no-match, which, respectively, means to not filter
110+ -- the email trigger by its from address, filter it if the email from address
111+ -- matches the given regex, or filter if it doesn't match the given regex.
112+ --
113+ -- /Default:/ always.
114+ efFromEmailMode :: Lens' a (Maybe MatchMode)
115+
116+ -- | The regex to be used when from_email_mode is match or no-match.
117+ -- It is a required parameter on such cases.
118+ efFromEmailRegex :: Lens' a (Maybe Text)
119+
120+ efSubjectMode = emailFilterInfo.efSubjectMode'
121+ efSubjectRegex = emailFilterInfo.efSubjectRegex'
122+ efBodyMode = emailFilterInfo.efBodyMode'
123+ efBodyRegex = emailFilterInfo.efBodyRegex'
124+ efFromEmailMode = emailFilterInfo.efFromEmailMode'
125+ efFromEmailRegex = emailFilterInfo.efFromEmailRegex'
126+
127+instance (QueryLike a, ToJSON a, HasEmailFilterInfo a)
128+ => HasEmailFilterInfo (Request a s b) where
129+ emailFilterInfo = upd.emailFilterInfo
130+
131+instance HasEmailFilterInfo EmailFilterInfo where
132+ emailFilterInfo = id
133+
134+data EmailFilter = EmailFilter
135+ { _efId :: EmailFilterId
136+ , _efInfo :: EmailFilterInfo
137+ } deriving (Eq, Show)
138+
139+instance FromJSON EmailFilter where
140+ parseJSON = withObject "email_filter" $ \o ->
141+ EmailFilter <$> parseJSON (Object o)
142+ <*> o .: "id"
143+
144+instance ToJSON EmailFilter where
145+ toJSON e = Object (x <> y)
146+ where
147+ Object x = toJSON (_efInfo e)
148+ Object y = object ["id" .= _efId e]
149+
150+instance HasEmailFilterInfo EmailFilter where
151+ emailFilterInfo = lens _efInfo (\e x -> e { _efInfo = x })
152+
153+-- | The email filter ID.
154+makeLens "_efId" ''EmailFilter
155+
156+-- | Create a new Email Filter for the specified service.
157+--
158+-- @POST \/services\/\:service_id\/email_filters@
159+--
160+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/email_filters/create>
161+createEmailFilter :: ServiceId -> Request EmailFilterInfo s EmailFilter
162+createEmailFilter s = mk def & meth .~ POST & path .~ filters s
163+
164+-- | Update an existing Email Filter.
165+--
166+-- @PUT \/services\/\:service_id\/email_filters\/\:id@
167+--
168+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/email_filters/update>
169+updateEmailFilter :: ServiceId
170+ -> EmailFilterId
171+ -> Request EmailFilterInfo s Empty
172+updateEmailFilter s e = mk def & meth .~ PUT & path .~ filters s % e
173+
174+-- | Delete an existing Email Filter.
175+--
176+-- @DELETE \/services\/\:service_id\/email_filters\/\:id@
177+--
178+-- /See:/ <http://developer.pagerduty.com/documentation/rest/services/email_filters/delete>
179+deleteEmailFilter :: ServiceId -> EmailFilterId -> Request Empty s Empty
180+deleteEmailFilter s e = empty & meth .~ DELETE & path .~ filters s % e
src/Network/PagerDuty/REST/Users.hsnew file+384
@@ -0,0 +1,384 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExtendedDefaultRules #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
8+
9+-- Module : Network.PagerDuty.REST.Users
10+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
11+-- License : This Source Code Form is subject to the terms of
12+-- the Mozilla Public License, v. 2.0.
13+-- A copy of the MPL can be found in the LICENSE file or
14+-- you can obtain it at http://mozilla.org/MPL/2.0/.
15+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
16+-- Stability : experimental
17+-- Portability : non-portable (GHC extensions)
18+
19+-- | Access and manipulate user data for your PagerDuty account. When a user is
20+-- shown inlined in other resources, a deleted user will have its @html_url@ attribute
21+-- set to null.
22+--
23+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users>
24+module Network.PagerDuty.REST.Users
25+ (
26+ -- * List Users
27+ ListUsers
28+ , listUsers
29+ , luQuery
30+
31+ -- * List On Call Users
32+ , listOnCallUsers
33+
34+ -- * Create User
35+ , CreateUser
36+ , createUser
37+ , cuName
38+ , cuEmail
39+ , cuRole
40+ , cuJobTitle
41+ , cuTimeZone
42+
43+ -- * Get User
44+ , getUser
45+
46+ -- * Get User On Call
47+ , getUserOnCall
48+
49+ -- * Update User
50+ , UpdateUser
51+ , updateUser
52+ , uuName
53+ , uuEmail
54+ , uuRole
55+ , uuJobTitle
56+ , uuTimeZone
57+
58+ -- * Delete User
59+ , deleteUser
60+
61+ -- * Types
62+ , PolicyInfo
63+ , piId
64+ , piName
65+
66+ , OnCall
67+ , ocLevel
68+ , ocStart
69+ , ocEnd
70+ , ocEscalationPolicy
71+
72+ , Role (..)
73+
74+ , HasUserInfo (..)
75+ , UserInfo
76+ , User
77+ , uRole
78+ , uAvatarUrl
79+ , uUserUrl
80+ , uInvitationSent
81+ , uJobTitle
82+ ) where
83+
84+import Control.Applicative hiding (empty)
85+import Control.Lens hiding ((.=))
86+import Data.Aeson
87+import Data.Default.Class
88+import Data.Monoid
89+import Data.Text (Text)
90+import Data.Time
91+import Network.HTTP.Types
92+import Network.PagerDuty.Internal.TH
93+import Network.PagerDuty.Internal.Types
94+
95+default (Path)
96+
97+users :: Path
98+users = "users"
99+
100+includes :: Query
101+includes =
102+ [ ("include[]", Just "contact_methods")
103+ , ("include[]", Just "notification_rules")
104+ ]
105+
106+data UserInfo = UserInfo
107+ { _uId' :: UserId
108+ , _uName' :: Text
109+ , _uEmail' :: Address
110+ , _uColor' :: Text
111+ , _uTimeZone' :: TZ
112+ } deriving (Eq, Show)
113+
114+deriveRecord ''UserInfo
115+
116+class HasUserInfo a where
117+ userInfo :: Lens' a UserInfo
118+
119+ -- | The id of the user.
120+ uId :: Lens' a UserId
121+
122+ -- | The name of the user.
123+ uName :: Lens' a Text
124+
125+ -- | The user's email address.
126+ uEmail :: Lens' a Address
127+
128+ -- | The color used to represent the user in schedules.
129+ uColor :: Lens' a Text
130+
131+ -- | The user's personal time zone.
132+ uTimeZone :: Lens' a TimeZone
133+
134+ uId = userInfo.uId'
135+ uName = userInfo.uName'
136+ uEmail = userInfo.uEmail'
137+ uColor = userInfo.uColor'
138+ uTimeZone = userInfo.uTimeZone'._TZ
139+
140+instance (QueryLike a, ToJSON a, HasUserInfo a)
141+ => HasUserInfo (Request a s b) where
142+ userInfo = upd.userInfo
143+
144+instance HasUserInfo UserInfo where
145+ userInfo = id
146+
147+data PolicyInfo = PolicyInfo
148+ { _piId :: EscalationPolicyId
149+ , _piName :: Text
150+ } deriving (Eq, Show)
151+
152+deriveRecord ''PolicyInfo
153+
154+data OnCall = OnCall
155+ { _ocLevel :: !Int
156+ , _ocStart :: Maybe Date
157+ , _ocEnd :: Maybe Date
158+ , _ocEscalationPolicy :: PolicyInfo
159+ } deriving (Eq, Show)
160+
161+deriveRecord ''OnCall
162+
163+data Role
164+ = RoleAdmin
165+ | RoleUser
166+ | RoleLimitedUser
167+ deriving (Eq, Show)
168+
169+deriveNullaryWith (dropped 4 underscored) ''Role
170+
171+instance Default Role where
172+ def = RoleUser
173+
174+data User = User
175+ { _uInfo :: UserInfo
176+ , _uRole :: !Role
177+ , _uAvatarUrl :: Text
178+ , _uUserUrl :: Text
179+ , _uInvitationSent' :: !Bool'
180+ , _uJobTitle :: Maybe Text
181+ } deriving (Eq, Show)
182+
183+ -- FIXME: add notification_rules, contact_methods, on_call
184+
185+makeLenses ''User
186+
187+instance FromJSON User where
188+ parseJSON = withObject "user" $ \o ->
189+ User <$> parseJSON (Object o)
190+ <*> o .: "role"
191+ <*> o .: "avatar_url"
192+ <*> o .: "user_url"
193+ <*> o .: "invitation_sent"
194+ <*> o .:? "job_title"
195+
196+instance ToJSON User where
197+ toJSON u = Object (x <> y)
198+ where
199+ Object x = toJSON (_uInfo u)
200+ Object y = object
201+ [ "role" .= _uRole u
202+ , "avatar_url" .= _uAvatarUrl u
203+ , "user_url" .= _uUserUrl u
204+ , "invitation_sent" .= _uInvitationSent' u
205+ , "job_title" .= _uJobTitle u
206+ ]
207+
208+instance HasUserInfo User where
209+ userInfo = uInfo
210+
211+uInvitationSent :: Lens' User Bool
212+uInvitationSent = uInvitationSent'._B
213+
214+newtype ListUsers = ListUsers
215+ { _luQuery' :: Maybe Text
216+ }
217+
218+queryRequest ''ListUsers
219+
220+instance Paginate ListUsers
221+
222+-- | Filters the result, showing only the users whose names or email addresses
223+-- match the query.
224+luQuery :: Lens' (Request ListUsers s b) (Maybe Text)
225+luQuery = upd.luQuery'
226+
227+-- | List users of your PagerDuty account, optionally filtered by a search query.
228+--
229+-- @GET \/users@
230+--
231+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/list>
232+listUsers :: Request ListUsers s [User]
233+listUsers =
234+ mk ListUsers
235+ { _luQuery' = Nothing
236+ } & path .~ users
237+ & query .~ includes
238+
239+-- | List all the existing escalation policies with currently on-call users.
240+--
241+-- If the start and end of an on-call object are null, then the user is always
242+-- on-call for an escalation policy level.
243+--
244+-- @GET \/escalation_policies\/on_call@
245+--
246+-- /See:/ <http://developer.pagerduty.com/documentation/rest/escalation_policies/on_call>
247+listOnCallUsers :: Request ListUsers s [User]
248+listOnCallUsers = listUsers & path .~ users % "on_call"
249+
250+-- | Get information about an existing user.
251+--
252+-- @GET \/users\/\:id@
253+--
254+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/show>
255+getUser :: UserId -> Request Empty s User
256+getUser u = empty & path .~ users % u & query .~ includes
257+
258+-- | Get a user object with that user's current on-call status. If the on-call
259+-- object is an empty list, the user is never on-call.
260+--
261+-- If the start and end of an on-call object are null, then the user is always
262+-- on-call for an escalation policy level.
263+--
264+-- @GET \/users\/\:id\/on_call@
265+--
266+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/show_on_call>
267+getUserOnCall :: UserId -> Request Empty s User
268+getUserOnCall u = empty & path .~ users % u % "on_call" & query .~ includes
269+
270+data CreateUser = CreateUser
271+ { _cuName' :: Text
272+ , _cuEmail' :: Address
273+ , _cuRole' :: Maybe Role
274+ , _cuJobTitle' :: Maybe Text
275+ , _cuTimeZone' :: Maybe TZ
276+ } deriving (Eq, Show)
277+
278+jsonRequest ''CreateUser
279+
280+-- | The name of the user.
281+cuName :: Lens' (Request CreateUser s b) Text
282+cuName = upd.cuName'
283+
284+-- | The email of the user. The newly created user will receive an email asking
285+-- to confirm the subscription.
286+cuEmail :: Lens' (Request CreateUser s b) Address
287+cuEmail = upd.cuEmail'
288+
289+-- | The user's role.
290+cuRole :: Lens' (Request CreateUser s b) (Maybe Role)
291+cuRole = upd.cuRole'
292+
293+-- | The job title of the user.
294+cuJobTitle :: Lens' (Request CreateUser s b) (Maybe Text)
295+cuJobTitle = upd.cuJobTitle'
296+
297+-- | The time zone the user is in. If not specified, the time zone of the
298+-- account making the API call will be used.
299+cuTimeZone :: Lens' (Request CreateUser s b) (Maybe TimeZone)
300+cuTimeZone = upd.cuTimeZone'.mapping _TZ
301+
302+-- | Create a new user for your account. An invite email will be sent asking
303+-- the user to choose a password.
304+--
305+-- @POST \/users@
306+--
307+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/create>
308+createUser :: RequesterId
309+ -> Text -- ^ 'cuName'
310+ -> Address -- ^ 'cuEmail'
311+ -> Request CreateUser s User
312+createUser r n e = auth (createUserBasic n e) & query .~ [("requester_id", r)]
313+
314+-- | A version of 'disableService' which uses HTTP Basic authentication and
315+-- doesn't require a 'RequesterId'.
316+createUserBasic :: Text -- ^ 'cuName'
317+ -> Address -- ^ 'cuEmail'
318+ -> Request CreateUser Basic User
319+createUserBasic n e =
320+ mk CreateUser
321+ { _cuName' = n
322+ , _cuEmail' = e
323+ , _cuRole' = Nothing
324+ , _cuJobTitle' = Nothing
325+ , _cuTimeZone' = Nothing
326+ } & meth .~ POST
327+ & path .~ users
328+
329+data UpdateUser = UpdateUser
330+ { _uuName' :: Maybe Text
331+ , _uuEmail' :: Maybe Address
332+ , _uuRole' :: Maybe Role
333+ , _uuJobTitle' :: Maybe Text
334+ , _uuTimeZone' :: Maybe TZ
335+ } deriving (Eq, Show)
336+
337+jsonRequest ''UpdateUser
338+
339+-- | The name of the user.
340+uuName :: Lens' (Request UpdateUser s b) (Maybe Text)
341+uuName = upd.uuName'
342+
343+-- | The email of the user. The newly created user will receive an email asking
344+-- to confirm the subscription.
345+uuEmail :: Lens' (Request UpdateUser s b) (Maybe Address)
346+uuEmail = upd.uuEmail'
347+
348+-- | The user's role.
349+uuRole :: Lens' (Request UpdateUser s b) (Maybe Role)
350+uuRole = upd.uuRole'
351+
352+-- | The job title of the user.
353+uuJobTitle :: Lens' (Request UpdateUser s b) (Maybe Text)
354+uuJobTitle = upd.uuJobTitle'
355+
356+-- | The time zone the user is in. If not specified, the time zone of the
357+-- account making the API call will be used.
358+uuTimeZone :: Lens' (Request UpdateUser s b) (Maybe TimeZone)
359+uuTimeZone = upd.uuTimeZone'.mapping _TZ
360+
361+-- | Update an existing user.
362+--
363+-- @PUT \/users\/\:id@
364+--
365+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/update>
366+updateUser :: UserId -> Request UpdateUser s User
367+updateUser u =
368+ mk UpdateUser
369+ { _uuName' = Nothing
370+ , _uuEmail' = Nothing
371+ , _uuRole' = Nothing
372+ , _uuJobTitle' = Nothing
373+ , _uuTimeZone' = Nothing
374+ } & meth .~ PUT
375+ & path .~ users % u
376+
377+-- | Remove an existing user.
378+--
379+-- @DELETE \/users\/\:id@
380+--
381+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/delete>
382+deleteUser :: UserId -> Request Empty s Empty
383+deleteUser u = empty & meth .~ DELETE & path .~ users % u
384+-- FIXME: deal with conflict errors
src/Network/PagerDuty/REST/Users/ContactMethods.hsnew file+236
@@ -0,0 +1,236 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
6+
7+-- Module : Network.PagerDuty.REST.Users.ContactMethods
8+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9+-- License : This Source Code Form is subject to the terms of
10+-- the Mozilla Public License, v. 2.0.
11+-- A copy of the MPL can be found in the LICENSE file or
12+-- you can obtain it at http://mozilla.org/MPL/2.0/.
13+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14+-- Stability : experimental
15+-- Portability : non-portable (GHC extensions)
16+
17+-- | Contact methods describe the various channels (phone numbers, SMS numbers
18+-- and email addresses) used to contact a user when an incident is assigned to
19+-- the user.
20+--
21+-- Access and manipulate the contact methods for a user.
22+--
23+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods>
24+module Network.PagerDuty.REST.Users.ContactMethods
25+ (
26+ -- * List Contacts
27+ listContacts
28+
29+ -- * Create Contact
30+ , CreateContact
31+ , createContact
32+ , ccType
33+ , ccAddress
34+ , ccCountryCode
35+ , ccLabel
36+ , ccSendShortEmail
37+
38+ -- * Get Contact
39+ , getContact
40+
41+ -- * Update Contact
42+ , UpdateContact
43+ , updateContact
44+ , ucAddress
45+ , ucCountryCode
46+ , ucLabel
47+ , ucSendShortEmail
48+
49+ -- * Delete Contact
50+ , deleteContact
51+
52+ -- * Types
53+ , ContactType (..)
54+
55+ , HasContact (..)
56+ , Contact
57+ ) where
58+
59+import Control.Lens
60+import Data.Aeson.Lens
61+import Data.Text (Text)
62+import Network.HTTP.Types
63+import Network.PagerDuty.Internal.TH
64+import Network.PagerDuty.Internal.Types
65+
66+default (Path)
67+
68+contacts :: UserId -> Path
69+contacts u = "users" % u % "contact_methods"
70+
71+data ContactType
72+ = SMS
73+ | Phone
74+ | Email
75+ deriving (Eq, Show)
76+
77+deriveNullary ''ContactType
78+
79+data Contact = Contact
80+ { _cId :: ContactId
81+ , _cUserId :: UserId
82+ , _cLabel :: Text
83+ , _cAddress :: Address
84+ , _cType :: !ContactType
85+ , _cCountryCode :: Maybe Int
86+ , _cSendShortEmail :: Maybe Bool'
87+ , _cBlacklisted :: Maybe Bool'
88+ } deriving (Eq, Show)
89+
90+deriveJSON ''Contact
91+makeClassy ''Contact
92+
93+-- | List existing contact methods for the specified user.
94+--
95+-- @GET \/users\/\:user_id\/contact_methods@
96+--
97+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods/list>
98+listContacts :: UserId -> Request Empty s [Contact]
99+listContacts u = empty & path .~ contacts u
100+
101+-- | Get details for a contact method.
102+--
103+-- @GET \/users\/\:user_id\/contact_methods\/\:id@
104+--
105+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods/show>
106+getContact :: UserId -> ContactId -> Request Empty s Contact
107+getContact u c = empty & path .~ contacts u % c
108+
109+data CreateContact = CreateContact
110+ { _ccType' :: !ContactType
111+ , _ccAddress' :: Address
112+ , _ccCountryCode' :: Maybe Int
113+ , _ccLabel' :: Maybe Text
114+ , _ccSendShortEmail' :: Maybe Bool'
115+ } deriving (Eq, Show)
116+
117+jsonRequest ''CreateContact
118+
119+-- | The type of the contact method.
120+ccType :: Lens' (Request CreateContact s b) ContactType
121+ccType = upd.ccType'
122+
123+-- | The id of the contact method. For 'SMS' and 'Phone' it is the number, and for
124+-- 'Email' it is the email address.
125+ccAddress :: Lens' (Request CreateContact s b) Address
126+ccAddress = upd.ccAddress'
127+
128+-- | The number code for your country. Not used for 'Email'.
129+--
130+-- /Default:/ 1
131+ccCountryCode :: Lens' (Request CreateContact s b) (Maybe Int)
132+ccCountryCode = upd.ccCountryCode'
133+
134+-- | A human friendly label for the contact method.
135+--
136+-- /Example:/ "Home Phone", "Work Email", etc.
137+--
138+-- /Default:/ The type of the contact method and the address (with country code
139+-- for phone numbers).
140+ccLabel :: Lens' (Request CreateContact s b) (Maybe Text)
141+ccLabel = upd.ccLabel'
142+
143+-- | Send an abbreviated email message instead of the standard email
144+-- output. Useful for email-to-SMS gateways and email based pagers. Only valid for
145+-- 'Email' contact methods.
146+--
147+-- /Default:/ false.
148+ccSendShortEmail :: Lens' (Request CreateContact s b) (Maybe Bool)
149+ccSendShortEmail = upd.ccSendShortEmail'.mapping _B
150+
151+-- | Create a new contact method for a given user. A contact method can be one
152+-- of the following types: SMS, email, and phone. push_notification contact
153+-- methods cannot be created using this API, they are added automatically during
154+-- device registration.
155+--
156+-- @POST \/users\/\:user_id\/contact_methods@
157+--
158+-- /Note:/ The contact info must be unique.
159+--
160+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods/create>
161+createContact :: UserId
162+ -> ContactType
163+ -> Address
164+ -> Request CreateContact s Contact
165+createContact u t a =
166+ mk CreateContact
167+ { _ccType' = t
168+ , _ccAddress' = a
169+ , _ccCountryCode' = Nothing
170+ , _ccLabel' = Nothing
171+ , _ccSendShortEmail' = Nothing
172+ } & meth .~ POST
173+ & path .~ contacts u
174+ & unwrap .~ key "contact_method"
175+
176+data UpdateContact = UpdateContact
177+ { _ucAddress' :: Maybe Address
178+ , _ucCountryCode' :: Maybe Int
179+ , _ucLabel' :: Maybe Text
180+ , _ucSendShortEmail' :: Maybe Bool'
181+ } deriving (Eq, Show)
182+
183+jsonRequest ''UpdateContact
184+
185+-- | The id of the contact method. For 'SMS' and 'Phone' it is the number, and for
186+-- 'Email' it is the email address.
187+ucAddress :: Lens' (Request UpdateContact s b) (Maybe Address)
188+ucAddress = upd.ucAddress'
189+
190+-- | The number code for your country. Not used for 'Email'.
191+--
192+-- /Default:/ 1
193+ucCountryCode :: Lens' (Request UpdateContact s b) (Maybe Int)
194+ucCountryCode = upd.ucCountryCode'
195+
196+-- | A human friendly label for the contact method.
197+--
198+-- /Example:/ "Home Phone", "Work Email", etc.
199+--
200+-- /Default:/ The type of the contact method and the address (with country code
201+-- for phone numbers).
202+ucLabel :: Lens' (Request UpdateContact s b) (Maybe Text)
203+ucLabel = upd.ucLabel'
204+
205+-- | Send an abbreviated email message instead of the standard email
206+-- output. Useful for email-to-SMS gateways and email based pagers. Only valid for
207+-- 'Email' contact methods.
208+--
209+-- /Default:/ false.
210+ucSendShortEmail :: Lens' (Request UpdateContact s b) (Maybe Bool)
211+ucSendShortEmail = upd.ucSendShortEmail'.mapping _B
212+
213+-- | Update an existing contact method of a given user. Note that you cannot
214+-- change the type of an existing method.
215+--
216+-- @PUT \/users\/\:user_id\/contact_methods\/\:id@
217+--
218+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods/update>
219+updateContact :: UserId -> ContactId -> Request UpdateContact s Contact
220+updateContact u c =
221+ mk UpdateContact
222+ { _ucAddress' = Nothing
223+ , _ucCountryCode' = Nothing
224+ , _ucLabel' = Nothing
225+ , _ucSendShortEmail' = Nothing
226+ } & meth .~ PUT
227+ & path .~ contacts u % c
228+ & unwrap .~ key "contact_method"
229+
230+-- | Remove a contact method and any corresponding notification rules.
231+--
232+-- @DELETE \/users\/\:user_id\/contact_methods\/\:id@
233+--
234+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/contact_methods/delete>
235+deleteContact :: UserId -> ContactId -> Request Empty s Empty
236+deleteContact u c = empty & meth .~ DELETE & path .~ contacts u % c
src/Network/PagerDuty/REST/Users/NotificationRules.hsnew file+164
@@ -0,0 +1,164 @@
1+{-# LANGUAGE ExtendedDefaultRules #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
6+
7+-- Module : Network.PagerDuty.REST.Users.NotificationRules
8+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9+-- License : This Source Code Form is subject to the terms of
10+-- the Mozilla Public License, v. 2.0.
11+-- A copy of the MPL can be found in the LICENSE file or
12+-- you can obtain it at http://mozilla.org/MPL/2.0/.
13+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14+-- Stability : experimental
15+-- Portability : non-portable (GHC extensions)
16+
17+-- | When a user is assigned an incident, the user's notification rules are
18+-- used to determine which contact method will be used to alert the user and how
19+-- long after the incident to do so.
20+--
21+-- Access and manipulate the notification rules for a user.
22+--
23+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules>
24+module Network.PagerDuty.REST.Users.NotificationRules
25+ (
26+ -- * List Rules
27+ listRules
28+
29+ -- * Create Rule
30+ , CreateRule
31+ , createRule
32+ , crStartDelayInMinutes
33+ , crContactMethodId
34+
35+ -- * Get Rule
36+ , getRule
37+
38+ -- * Update Rule
39+ , UpdateRule
40+ , updateRule
41+ , urStartDelayInMinutes
42+ , urContactMethodId
43+
44+ -- * Delete Rule
45+ , deleteRule
46+
47+ -- * Types
48+ , Rule
49+ , rId
50+ , rStartDelayInMinutes
51+ , rContactMethod
52+ ) where
53+
54+import Control.Lens
55+import Data.Aeson.Lens
56+import Network.HTTP.Types
57+import Network.PagerDuty.REST.Users.ContactMethods
58+import Network.PagerDuty.Internal.TH
59+import Network.PagerDuty.Internal.Types
60+
61+default (Path)
62+
63+rules :: UserId -> Path
64+rules u = "users" % u % "notification_rules"
65+
66+data Rule = Rule
67+ { _rId :: NotificationRuleId
68+ , _rStartDelayInMinutes :: !Int
69+ , _rContactMethod :: Contact
70+ } deriving (Eq, Show)
71+
72+deriveJSON ''Rule
73+makeLenses ''Rule
74+
75+instance HasContact Rule where
76+ contact = rContactMethod
77+
78+-- | List existing notification rules for the specified user.
79+--
80+-- @GET \/users\/\:user_id\/notification_rules@
81+--
82+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules/list>
83+listRules :: UserId -> Request Empty s Rule
84+listRules u = empty & path .~ rules u
85+
86+-- | Get details for a notification rule.
87+--
88+-- @GET \/users\/\:user_id\/notification_rules\/\:id@
89+--
90+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules/show>
91+getRule :: UserId -> NotificationRuleId -> Request Empty s Rule
92+getRule u n = empty & path .~ rules u % n
93+
94+data CreateRule = CreateRule
95+ { _crStartDelayInMinutes' :: !Int
96+ , _crContactMethodId' :: ContactId
97+ } deriving (Eq, Show)
98+
99+jsonRequest ''CreateRule
100+
101+-- | Number of minutes it will take for the notification rule to be activated
102+-- (from the time the incident is assigned to the owning user) and an alert be
103+-- fired.
104+crStartDelayInMinutes :: Lens' (Request CreateRule s b) Int
105+crStartDelayInMinutes = upd.crStartDelayInMinutes'
106+
107+-- | The id of the contact method.
108+crContactMethodId :: Lens' (Request CreateRule s b) ContactId
109+crContactMethodId = upd.crContactMethodId'
110+
111+-- | Create a new notification rule for the specified user.
112+--
113+-- @POST \/users\/\:user_id\/notification_rules@
114+--
115+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules/create>
116+createRule :: UserId -> ContactId -> Int -> Request CreateRule s Rule
117+createRule u c n =
118+ mk CreateRule
119+ { _crStartDelayInMinutes' = n
120+ , _crContactMethodId' = c
121+ } & meth .~ POST
122+ & path .~ rules u
123+ & unwrap .~ key "notification_rule"
124+
125+data UpdateRule = UpdateRule
126+ { _urStartDelayInMinutes' :: Maybe Int
127+ , _urContactMethodId' :: Maybe ContactId
128+ } deriving (Eq, Show)
129+
130+jsonRequest ''UpdateRule
131+
132+-- | Number of minutes it will take for the notification rule to be activated
133+-- (from the time the incident is assigned to the owning user) and an alert be
134+-- fired.
135+urStartDelayInMinutes :: Lens' (Request UpdateRule s b) (Maybe Int)
136+urStartDelayInMinutes = upd.urStartDelayInMinutes'
137+
138+-- | The id of the contact method.
139+urContactMethodId :: Lens' (Request UpdateRule s b) (Maybe ContactId)
140+urContactMethodId = upd.urContactMethodId'
141+
142+-- | Update an existing notification rule.
143+--
144+-- @PUT \/users\/\:user_id\/notification_rules\/\:id@
145+--
146+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules/update>
147+updateRule :: UserId -> NotificationRuleId -> Request UpdateRule s Rule
148+updateRule u r =
149+ mk UpdateRule
150+ { _urStartDelayInMinutes' = Nothing
151+ , _urContactMethodId' = Nothing
152+ } & meth .~ PUT
153+ & path .~ rules u % r
154+ & unwrap .~ key "notification_rule"
155+
156+-- | Remove a notification rule.
157+--
158+-- @DELETE \/users\/\:user_id\/notification_rules\/\:id@
159+--
160+-- /See:/ <http://developer.pagerduty.com/documentation/rest/users/notification_rules/delete>
161+deleteRule :: UserId
162+ -> NotificationRuleId
163+ -> Request Empty s Empty
164+deleteRule u n = empty & meth .~ DELETE & path .~ rules u % n
src/Network/PagerDuty/REST/Webhooks.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+-- Module : Network.PagerDuty.REST.Webhooks
6+-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
7+-- License : This Source Code Form is subject to the terms of
8+-- the Mozilla Public License, v. 2.0.
9+-- A copy of the MPL can be found in the LICENSE file or
10+-- you can obtain it at http://mozilla.org/MPL/2.0/.
11+-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
12+-- Stability : experimental
13+-- Portability : non-portable (GHC extensions)
14+
15+-- | Webhooks let you recieve HTTP callbacks when interesting events happen within
16+-- your PagerDuty account. Details surrounding the interesting event will be sent
17+-- via HTTP to a URL that you specify.
18+--
19+-- PagerDuty currently supports incident-based webhooks. After adding a webhook
20+-- URL to a PagerDuty service, the triggering of new incidents on that service
21+-- will cause outgoing webhook messages to be sent to that URL. In addition,
22+-- certain interesting changes to an incident's state will cause other types of
23+-- incident webhook messages to be sent: Generally, any change to the @status@ or
24+-- @assigned_to_user@ of an incident will cause an outgoing message to be sent.
25+--
26+-- /See:/ <http://developer.pagerduty.com/documentation/rest/webhooks>
27+module Network.PagerDuty.REST.Webhooks
28+ ( WebhookType (..)
29+ , Webhook
30+ , wId
31+ , wType
32+ , wCreatedOn
33+ ) where
34+
35+import Control.Applicative hiding (empty)
36+import Control.Lens
37+import Data.Aeson
38+import Data.ByteString.Conversion (ToByteString(..), toByteString')
39+import Data.Text (Text)
40+import qualified Data.Text.Encoding as Text
41+import Data.Time
42+import Network.PagerDuty.REST.Incidents
43+import Network.PagerDuty.Internal.TH
44+import Network.PagerDuty.Internal.Types
45+
46+data WebhookType
47+ = WTrigger
48+ -- ^ Sent when an incident is newly created/triggered.
49+ | WAcknowledge
50+ -- ^ Sent when an incident has had its status changed from triggered to
51+ -- acknowledged.
52+ | WUnacknowledge
53+ -- ^ Sent when an incident is unacknowledged due to timeout.
54+ | WResolve
55+ -- ^ Sent when an incident has been resolved.
56+ | WAssign
57+ -- ^ Sent when an incident has been manually reassigned to another user
58+ -- in a different escalation chain.
59+ | WEscalate
60+ -- ^ Sent when an incident has been escalated to another user in the
61+ -- same escalation chain.
62+ | WDelegate
63+ -- ^ Sent when an incident has been reassigned to another escalation chain.
64+ | WOther Text
65+ -- ^ /Note:/ As new types of incident actions are supported,
66+ -- new incident webhook message types will be added.
67+ deriving (Eq, Show)
68+
69+instance ToByteString WebhookType where
70+ builder = \case
71+ WTrigger -> "incident.trigger"
72+ WAcknowledge -> "incident.acknowledge"
73+ WUnacknowledge -> "incident.unacknowledge"
74+ WResolve -> "incident.resolve"
75+ WAssign -> "incident.assign"
76+ WEscalate -> "incident.escalate"
77+ WDelegate -> "incident.delegate"
78+ WOther t -> builder t
79+
80+instance FromJSON WebhookType where
81+ parseJSON = withText "status" $ pure . \case
82+ "incident.trigger" -> WTrigger
83+ "incident.acknowledge" -> WAcknowledge
84+ "incident.unacknowledge" -> WUnacknowledge
85+ "incident.resolve" -> WResolve
86+ "incident.assign" -> WAssign
87+ "incident.escalate" -> WEscalate
88+ "incident.delegate" -> WDelegate
89+ t -> (WOther t)
90+
91+instance ToJSON WebhookType where
92+ toJSON = String . Text.decodeUtf8 . toByteString'
93+
94+data Webhook = Webhook
95+ { _wId :: WebhookId
96+ , _wType :: !WebhookType
97+ , _wCreatedOn :: !Date
98+ , _wData :: Incident
99+ } deriving (Eq, Show)
100+
101+deriveJSON ''Webhook
102+
103+instance HasIncident Webhook where
104+ incident = lens _wData (\s x -> s { _wData = x })
105+
106+-- | Uniquely identifies this outgoing webhook message; can be used for
107+-- idempotency when processing the messages.
108+makeLens "_wId" ''Webhook
109+
110+-- | The webhook message type.
111+makeLens "_wType" ''Webhook
112+
113+-- | The date/time when the incident changed state.
114+wCreatedOn :: Lens' Webhook UTCTime
115+wCreatedOn = lens _wCreatedOn (\i x -> i { _wCreatedOn = x }) . _D
src/Network/PagerDuty/Types.hsdeleted−126
@@ -1,126 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE TemplateHaskell #-}
6−
7−-- Module : Network.PagerDuty.Types
8−-- Copyright : (c) 2013-2014 Brendan Hay <brendan.g.hay@gmail.com>
9−-- License : This Source Code Form is subject to the terms of
10−-- the Mozilla Public License, v. 2.0.
11−-- A copy of the MPL can be found in the LICENSE file or
12−-- you can obtain it at http://mozilla.org/MPL/2.0/.
13−-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
14−-- Stability : experimental
15−-- Portability : non-portable (GHC extensions)
16−
17−module Network.PagerDuty.Types
18−where
19−
20−import Control.Applicative
21−import Control.Monad.Reader
22−import Data.Aeson
23−import Data.ByteString (ByteString)
24−import qualified Data.HashMap.Strict as H
25−import Data.String
26−import Data.Text (Text)
27−import GHC.Generics
28−import Network.HTTP.Client (Manager)
29−import Network.PagerDuty.Internal
30−
31−
32−data Env a
33− = Env !Manager
34− | TokenEnv !Host !Token !Manager
35− | BasicEnv !Host !BasicAuth !Manager
36−
37−type PagerDuty a b = ReaderT (Env a) IO b
38−
39−
40−newtype SubDomain = SubDomain { subDomain :: ByteString }
41− deriving (Eq, Show, IsString)
42−
43−
44−data BasicAuth = BasicAuth !ByteString !ByteString
45− deriving Eq
46−
47−newtype Token = Token ByteString
48− deriving (Eq, IsString)
49−
50−
51−data Authenticated a
52−data UnAuthenticated
53−
54−
55−type Host = ByteString
56−
57−
58−newtype Code = Code Integer
59− deriving (Eq, Show, Generic)
60−
61−instance FromJSON Code
62−instance ToJSON Code
63−
64−message :: Code -> Text
65−message (Code c) = case c of
66− 2000 -> "Internal Error"
67− 2001 -> "Invalid Input Provided"
68− 2002 -> "Arguments Caused Error"
69− 2003 -> "Missing Arguments"
70− 2004 -> "Invalid 'since' or 'until' Parameter Values"
71− 2005 -> "Invalid Query Date Range"
72− 2006 -> "Authentication Failed"
73− 2007 -> "Account Not Found"
74− 2008 -> "Account Locked"
75− 2009 -> "Only HTTPS Allowed For This Call"
76− 2010 -> "Access Denied"
77− 2011 -> "The action requires a 'requester_id' to be specified"
78− 2012 -> "Your account is expired and cannot use the API"
79− _ -> "Unrecognised error code"
80−
81−
82−data Error
83− = Internal String
84− | External
85− { _message :: Text
86− , _code :: Code
87− , _errors :: [Text]
88− }
89− deriving (Eq, Show, Generic)
90−
91−instance ToJSON Error where toJSON = gToJson "_"
92−instance FromJSON Error where parseJSON = gFromJson "_"
93−
94−
95−newtype Key a = Key Text
96− deriving (Eq, Show, Generic, IsString)
97−
98−instance ToJSON (Key a)
99−instance FromJSON (Key a)
100−
101−data Service
102−data Incident
103−
104−type ServiceKey = Key Service
105−type IncidentKey = Key Incident
106−
107−
108−newtype Id a = Id Text
109− deriving (Eq, Show, Generic, IsString)
110−
111−instance ToJSON (Id a)
112−instance FromJSON (Id a)
113−
114−data Requester
115−
116−type ServiceId = Id Service
117−type RequesterId = Id Requester
118−
119−
120−data Empty = Empty
121−
122−instance ToJSON Empty where toJSON _ = object []
123−instance FromJSON Empty where
124− parseJSON (Object !o) | H.null o = pure Empty
125− | otherwise = mzero
126− parseJSON _ = mzero