@@ -13,24 +13,27 @@ import Universum
1313import Control.Concurrent (threadDelay )
1414import Control.Monad.Reader (ask )
1515
16- import Data.Aeson (FromJSON , ToJSON , Value , encode , parseJSON )
16+ import Data.Aeson (parseJSON )
1717import Data.Aeson.Text (encodeToLazyText )
18- import Data.Aeson.Types (Parser , parseEither )
1918import Data.List (nub )
20- import Network.HTTP.Simple (Request , addRequestHeader , getResponseBody , httpJSON , httpLBS ,
21- parseRequest_ , setRequestBasicAuth , setRequestBodyJSON ,
22- setRequestMethod , setRequestPath )
19+ import Network.HTTP.Simple (Request , getResponseBody , httpLBS , parseRequest_ )
20+
21+ import HttpLayer
2322
2423import DataSource.Types (Attachment (.. ), AttachmentContent (.. ), Comment (.. ),
2524 CommentBody (.. ), CommentId (.. ), Config (.. ),
2625 DeletedTicket (.. ), ExportFromTime (.. ), FromPageResultList (.. ),
2726 PageResultList (.. ), Ticket (.. ), TicketId (.. ), TicketInfo (.. ),
2827 TicketTag (.. ), TicketTags (.. ), User , UserId (.. ),
2928 ZendeskAPIUrl (.. ), ZendeskLayer (.. ), ZendeskResponse (.. ),
30- parseComments , parseTicket , renderTicketStatus , showURL )
29+ asksHTTPNetworkLayer , parseComments , renderTicketStatus , showURL )
3130
3231-- ./mitmproxy --mode reverse:https://iohk.zendesk.com -p 4001
3332
33+ ------------------------------------------------------------
34+ -- Zendesk layer
35+ ------------------------------------------------------------
36+
3437-- | The basic Zendesk layer.
3538-- The convention:
3639-- - get returns a single result (wrapped in @Maybe@)
@@ -65,6 +68,10 @@ emptyZendeskLayer = ZendeskLayer
6568 , zlExportTickets = \ _ -> error " Not implemented zlExportTickets!"
6669 }
6770
71+ ------------------------------------------------------------
72+ -- Zendesk functions
73+ ------------------------------------------------------------
74+
6875-- | Get single ticket info.
6976getTicketInfo
7077 :: (MonadIO m , MonadReader Config m )
@@ -75,7 +82,10 @@ getTicketInfo ticketId = do
7582
7683 let url = showURL $ TicketsURL ticketId
7784 let req = apiRequest cfg url
78- liftIO $ Just <$> apiCall parseTicket req
85+
86+ apiCall <- asksHTTPNetworkLayer hnlApiCall
87+
88+ Just <$> apiCall parseJSON req
7989
8090-- | Return list of deleted tickets.
8191listDeletedTickets
@@ -146,23 +156,28 @@ getExportedTickets
146156 -> m [TicketInfo ]
147157getExportedTickets time = do
148158 cfg <- ask
159+
160+ apiCall <- asksHTTPNetworkLayer hnlApiCall
161+
149162 let url = showURL $ ExportDataByTimestamp time
150163 let req = apiRequestAbsolute cfg url
151- iterateExportedTicketsWithDelay req
164+
165+ iterateExportedTicketsWithDelay req (apiCall parseJSON)
152166 where
153167
154168 iterateExportedTicketsWithDelay
155169 :: Request
170+ -> (Request -> m (PageResultList TicketInfo ))
156171 -> m [TicketInfo ]
157- iterateExportedTicketsWithDelay req = do
172+ iterateExportedTicketsWithDelay req apiCall = do
158173 cfg <- ask
159174
160- let go :: [TicketInfo ] -> Text -> IO [TicketInfo ]
175+ let go :: [TicketInfo ] -> Text -> m [TicketInfo ]
161176 go list' nextPage' = do
162- threadDelay $ 10 * 1000000 -- Wait, Zendesk allows for 10 per minute.
177+ liftIO $ threadDelay $ 10 * 1000000 -- Wait, Zendesk allows for 10 per minute.
163178
164179 let req' = apiRequestAbsolute cfg nextPage'
165- (PageResultList pagen nextPagen count) <- apiCall parseJSON req'
180+ (PageResultList pagen nextPagen count) <- apiCall req'
166181 case nextPagen of
167182 Just nextUrl -> if maybe False (>= 1000 ) count
168183 then go (list' <> pagen) nextUrl
@@ -171,45 +186,11 @@ getExportedTickets time = do
171186 Nothing -> pure (list' <> pagen)
172187
173188
174- (PageResultList page0 nextPage _) <- liftIO $ apiCall parseJSON req
189+ (PageResultList page0 nextPage _) <- apiCall req
175190 case nextPage of
176- Just nextUrl -> liftIO $ go page0 nextUrl
191+ Just nextUrl -> go page0 nextUrl
177192 Nothing -> pure page0
178193
179- -- | Iterate all the ticket pages and combine into a result.
180- iteratePages
181- :: forall m a . (MonadIO m , MonadReader Config m , FromPageResultList a )
182- => Request
183- -> m [a ]
184- iteratePages req = iteratePagesWithDelay 0 req
185-
186- -- | Iterate all the ticket pages and combine into a result. Wait for
187- -- some time in-between the requests.
188- iteratePagesWithDelay
189- :: forall m a . (MonadIO m , MonadReader Config m , FromPageResultList a )
190- => Int
191- -> Request
192- -> m [a ]
193- iteratePagesWithDelay seconds req = do
194- cfg <- ask
195-
196- let go :: [a ] -> Text -> IO [a ]
197- go list' nextPage' = do
198- -- Wait for @Int@ seconds.
199- threadDelay $ seconds * 1000000
200-
201- let req' = apiRequestAbsolute cfg nextPage'
202- (PageResultList pagen nextPagen _) <- apiCall parseJSON req'
203- case nextPagen of
204- Just nextUrl -> go (list' <> pagen) nextUrl
205- Nothing -> pure (list' <> pagen)
206-
207- (PageResultList page0 nextPage _) <- liftIO $ apiCall parseJSON req
208- case nextPage of
209- Just nextUrl -> liftIO $ go page0 nextUrl
210- Nothing -> pure page0
211-
212-
213194-- | Send API request to post comment
214195postTicketComment
215196 :: (MonadIO m , MonadReader Config m )
@@ -218,10 +199,14 @@ postTicketComment
218199 -> m ()
219200postTicketComment ticketInfo zendeskResponse = do
220201 cfg <- ask
202+
203+ addJsonBody <- asksHTTPNetworkLayer hnlAddJsonBody
204+ apiCall <- asksHTTPNetworkLayer hnlApiCall
205+
221206 let responseTicket = createResponseTicket (cfgAgentId cfg) ticketInfo zendeskResponse
222207 let url = showURL $ TicketsURL (zrTicketId zendeskResponse)
223208 let req = addJsonBody responseTicket (apiRequest cfg url)
224- void $ liftIO $ apiCall (pure . encodeToLazyText) req
209+ void $ apiCall (pure . encodeToLazyText) req
225210
226211-- | Create response ticket
227212createResponseTicket :: Integer -> TicketInfo -> ZendeskResponse -> Ticket
@@ -248,10 +233,12 @@ _getUser
248233_getUser = do
249234 cfg <- ask
250235
236+ apiCall <- asksHTTPNetworkLayer hnlApiCall
237+
251238 let url = showURL UserInfoURL
252239 let req = apiRequest cfg url
253240
254- liftIO $ apiCall parseJSON req
241+ apiCall parseJSON req
255242
256243-- | Given attachmentUrl, return attachment in bytestring
257244getAttachment
@@ -271,10 +258,12 @@ getTicketComments
271258getTicketComments tId = do
272259 cfg <- ask
273260
261+ apiCallSafe <- asksHTTPNetworkLayer hnlApiCallSafe
262+
274263 let url = showURL $ TicketCommentsURL tId
275264 let req = apiRequest cfg url
276265
277- result <- apiCallSafe parseComments req
266+ result <- apiCallSafe parseComments req
278267
279268 -- TODO(ks): For now return empty if there is an exception.
280269 -- After we have exception handling, we propagate this up.
@@ -283,51 +272,41 @@ getTicketComments tId = do
283272 Right r -> pure r
284273
285274------------------------------------------------------------
286- -- HTTP utility
275+ -- Utility
287276------------------------------------------------------------
288277
289- -- | Request PUT
290- addJsonBody :: ToJSON a => a -> Request -> Request
291- addJsonBody body req = setRequestBodyJSON body $ setRequestMethod " PUT" req
292-
293- -- | Make an api call
294- -- TODO(ks): Switch to @Either@.
295- apiCall :: FromJSON a => (Value -> Parser a ) -> Request -> IO a
296- apiCall parser req = do
297- putTextLn $ show req
298- v <- getResponseBody <$> httpJSON req
299- case parseEither parser v of
300- Right o -> pure o
301- Left e -> error $ " couldn't parse response "
302- <> toText e <> " \n " <> decodeUtf8 (encode v)
303-
304- -- | Make a safe api call.
305- apiCallSafe
306- :: forall m a . (MonadIO m , FromJSON a )
307- => (Value -> Parser a )
278+ -- | Iterate all the ticket pages and combine into a result.
279+ iteratePages
280+ :: forall m a . (MonadIO m , MonadReader Config m , FromPageResultList a )
281+ => Request
282+ -> m [a ]
283+ iteratePages req = iteratePagesWithDelay 0 req
284+
285+ -- | Iterate all the ticket pages and combine into a result. Wait for
286+ -- some time in-between the requests.
287+ iteratePagesWithDelay
288+ :: forall m a . (MonadIO m , MonadReader Config m , FromPageResultList a )
289+ => Int
308290 -> Request
309- -> m (Either String a )
310- apiCallSafe parser req = do
311- putTextLn $ show req
312- v <- getResponseBody <$> httpJSON req
313- pure $ parseEither parser v
314-
315- -- | General api request function
316- apiRequest :: Config -> Text -> Request
317- apiRequest Config {.. } u = setRequestPath (encodeUtf8 path) $
318- addRequestHeader " Content-Type" " application/json" $
319- setRequestBasicAuth
320- (encodeUtf8 cfgEmail <> " /token" )
321- (encodeUtf8 cfgToken) $
322- parseRequest_ (toString (cfgZendesk <> path))
323- where
324- path :: Text
325- path = " /api/v2" <> u
326-
327- -- | Api request but use absolute path
328- apiRequestAbsolute :: Config -> Text -> Request
329- apiRequestAbsolute Config {.. } u = addRequestHeader " Content-Type" " application/json" $
330- setRequestBasicAuth
331- (encodeUtf8 cfgEmail <> " /token" )
332- (encodeUtf8 cfgToken) $
333- parseRequest_ (toString u)
291+ -> m [a ]
292+ iteratePagesWithDelay seconds req = do
293+ cfg <- ask
294+
295+ apiCall <- asksHTTPNetworkLayer hnlApiCall
296+
297+ let go :: [a ] -> Text -> IO [a ]
298+ go list' nextPage' = do
299+ -- Wait for @Int@ seconds.
300+ threadDelay $ seconds * 1000000
301+
302+ let req' = apiRequestAbsolute cfg nextPage'
303+ (PageResultList pagen nextPagen _) <- apiCall parseJSON req'
304+ case nextPagen of
305+ Just nextUrl -> go (list' <> pagen) nextUrl
306+ Nothing -> pure (list' <> pagen)
307+
308+ (PageResultList page0 nextPage _) <- liftIO $ apiCall parseJSON req
309+ case nextPage of
310+ Just nextUrl -> liftIO $ go page0 nextUrl
311+ Nothing -> pure page0
312+
0 commit comments