Skip to content

Commit 0d57a90

Browse files
authored
[TSD-40] Send the message in to all requesters with no log in the ticket (#17)
* Add test cases * Send the message in to all requesters with no log in the ticket * Fix test cases * Create getZendeskResponses function to decompose processTicket * Filter goguen testnet tickets * Add response message * Minor fixes * Return single ZendeskResponse instead of list of responses * Inspect only latest attachment * Sort comment by id, inspect last element of the attachments * Fix flag * Minor fixes * [TSD-40] The impure version. * [TSD-40] The version with the pure function. Either is an option here. * Couple of minor fixes - Unwrap newtype so the request api will be valid - Make assignee_id field to be maybe since ticket could be unassigned in that case classifier will not be able to parse the json
1 parent 378bfea commit 0d57a90

File tree

5 files changed

+201
-87
lines changed

5 files changed

+201
-87
lines changed

src/DataSource/Http.hs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import Network.HTTP.Simple (Request, addRequestHeader, getResponseBody
2020

2121
import DataSource.Types (Attachment (..), AttachmentContent (..), Comment (..),
2222
CommentBody (..), CommentId (..), Config (..), IOLayer (..),
23-
Ticket (..), TicketId, TicketInfo (..), TicketList (..),
24-
TicketTag (..), User, UserId, ZendeskLayer (..),
25-
ZendeskResponse (..), parseComments, parseTickets,
23+
Ticket (..), TicketId(..), TicketInfo (..), TicketList (..),
24+
TicketTag (..), User, UserId(..), ZendeskLayer (..),
25+
ZendeskResponse (..), parseComments, parseTickets, parseTicket,
2626
renderTicketStatus)
2727

2828

@@ -80,8 +80,8 @@ getTicketInfo
8080
getTicketInfo ticketId = do
8181
cfg <- ask
8282

83-
let req = apiRequest cfg ("tickets/" <> show ticketId <> ".json")
84-
liftIO $ apiCall parseJSON req
83+
let req = apiRequest cfg ("tickets/" <> show (getTicketId ticketId) <> ".json")
84+
liftIO $ Just <$> apiCall parseTicket req
8585

8686

8787
-- | Return list of ticketIds that has been requested by config user.
@@ -92,7 +92,7 @@ listRequestedTickets
9292
listRequestedTickets userId = do
9393
cfg <- ask
9494

95-
let url = "/users/" <> show userId <> "/tickets/requested.json"
95+
let url = "/users/" <> show (getUserId userId) <> "/tickets/requested.json"
9696
let req = apiRequest cfg url
9797

9898
iterateTicketPages req
@@ -105,7 +105,7 @@ listAssignedTickets
105105
listAssignedTickets userId = do
106106
cfg <- ask
107107

108-
let url = "/users/" <> show userId <> "/tickets/assigned.json"
108+
let url = "/users/" <> show (getUserId userId) <> "/tickets/assigned.json"
109109
let req = apiRequest cfg url
110110

111111
iterateTicketPages req
@@ -175,7 +175,7 @@ getTicketComments
175175
-> m [Comment]
176176
getTicketComments tId = do
177177
cfg <- ask
178-
let req = apiRequest cfg ("tickets/" <> show tId <> "/comments.json")
178+
let req = apiRequest cfg ("tickets/" <> show (getTicketId tId) <> "/comments.json")
179179
liftIO $ apiCall parseComments req
180180

181181
------------------------------------------------------------

src/DataSource/Types.hs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ module DataSource.Types
2929
, UserName (..)
3030
, UserEmail (..)
3131
, parseComments
32+
, parseTicket
3233
, parseTickets
3334
, renderTicketStatus
3435
-- * General configuration
@@ -167,6 +168,9 @@ data Attachment = Attachment
167168
-- ^ Attachment size
168169
} deriving (Eq, Show)
169170

171+
instance Ord Attachment where
172+
compare a1 a2 = compare (aId a1) (aId a2)
173+
170174
instance Arbitrary Attachment where
171175
arbitrary = Attachment
172176
<$> arbitrary
@@ -217,6 +221,8 @@ data Comment = Comment
217221
-- ^ Author of comment
218222
} deriving (Eq, Show)
219223

224+
instance Ord Comment where
225+
compare c1 c2 = compare (cId c1) (cId c2)
220226

221227
instance Arbitrary Comment where
222228
arbitrary = Comment
@@ -275,7 +281,7 @@ newtype TicketStatus = TicketStatus
275281
data TicketInfo = TicketInfo
276282
{ tiId :: !TicketId -- ^ Id of an ticket
277283
, tiRequesterId :: !UserId -- ^ Id of the requester
278-
, tiAssigneeId :: !UserId -- ^ Id of the asignee
284+
, tiAssigneeId :: Maybe UserId -- ^ Id of the asignee
279285
, tiUrl :: !TicketURL -- ^ The ticket URL
280286
, tiTags :: !TicketTags -- ^ Tags associated with ticket
281287
, tiStatus :: !TicketStatus -- ^ The status of the ticket
@@ -369,12 +375,14 @@ data TicketTag
369375
= AnalyzedByScript -- ^ Ticket has been analyzed
370376
| AnalyzedByScriptV1_0 -- ^ Ticket has been analyzed by the version 1.0
371377
| NoKnownIssue -- ^ Ticket had no known issue
378+
| NoLogAttached -- ^ Log file not attached
372379

373380
-- | Defining it's own show instance to use it as tags
374381
renderTicketStatus :: TicketTag -> Text
375382
renderTicketStatus AnalyzedByScript = "analyzed-by-script"
376383
renderTicketStatus AnalyzedByScriptV1_0 = "analyzed-by-script-v1.0"
377384
renderTicketStatus NoKnownIssue = "no-known-issues"
385+
renderTicketStatus NoLogAttached = "no-log-files"
378386

379387
-- | JSON Parsing
380388
instance FromJSON Comment where
@@ -463,6 +471,9 @@ instance ToJSON CommentOuter where
463471
]
464472

465473

474+
parseTicket :: Value -> Parser TicketInfo
475+
parseTicket = withObject "ticket" $ \o -> o .: "ticket"
476+
466477
-- | Parse tickets
467478
parseTickets :: Value -> Parser TicketList
468479
parseTickets = withObject "tickets" $ \o ->

src/Lib.hs

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2-
{-# LANGUAGE OverloadedStrings #-}
3-
{-# LANGUAGE RecordWildCards #-}
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE RecordWildCards #-}
43

54
module Lib
65
( runZendeskMain
76
, collectEmails
7+
, getZendeskResponses
88
, processTicket
99
, processTickets
1010
, fetchTickets
1111
, showStatistics
12-
1312
, listAndSortTickets
1413
) where
1514

@@ -27,11 +26,10 @@ import DataSource (App, Attachment (..), AttachmentContent (..), Comme
2726
renderTicketStatus, runApp, tokenPath)
2827
import LogAnalysis.Classifier (extractErrorCodes, extractIssuesFromLogs,
2928
prettyFormatAnalysis, prettyFormatLogReadError,
30-
prettyFormatNoIssues)
29+
prettyFormatNoIssues, prettyFormatNoLogs)
3130
import LogAnalysis.KnowledgeCSVParser (parseKnowLedgeBase)
3231
import LogAnalysis.Types (ErrorCode (..), Knowledge, renderErrorCode, setupAnalysis)
3332
import Util (extractLogsFromZip)
34-
3533
------------------------------------------------------------
3634
-- Functions
3735
------------------------------------------------------------
@@ -79,29 +77,26 @@ collectEmails = do
7977
mapM_ extractEmailAddress ticketIds
8078

8179

82-
processTicket :: TicketId -> App [ZendeskResponse]
83-
processTicket ticketId = do
80+
processTicket :: TicketId -> App (Maybe ZendeskResponse)
81+
processTicket tId = do
8482

8583
-- We first fetch the function from the configuration
8684
getTicketInfo <- asksZendeskLayer zlGetTicketInfo
8785
printText <- asksIOLayer iolPrintText
8886

8987
printText "Processing single ticket"
9088

91-
ticketInfoM <- getTicketInfo ticketId
92-
93-
-- TODO(ks): Better exception handling.
94-
let ticketInfo = fromMaybe (error "Missing ticket info!") ticketInfoM
95-
96-
attachments <- getTicketAttachments ticketInfo
97-
98-
zendeskResponse <- mapM (inspectAttachment ticketInfo) attachments
99-
89+
mTicketInfo <- getTicketInfo tId
90+
getTicketComments <- asksZendeskLayer zlGetTicketComments
91+
comments <- getTicketComments tId
92+
let attachments = getAttachmentsFromComment comments
93+
let ticketInfo = fromMaybe (error "No ticket info") mTicketInfo
94+
zendeskResponse <- getZendeskResponses comments attachments ticketInfo
10095
postTicketComment <- asksZendeskLayer zlPostTicketComment
101-
_ <- mapM postTicketComment zendeskResponse
96+
whenJust zendeskResponse postTicketComment
10297

10398
printText "Process finished, please see the following url"
104-
printText $ "https://iohk.zendesk.com/agent/tickets/" <> show ticketId
99+
printText $ "https://iohk.zendesk.com/agent/tickets/" <> show tId
105100

106101
pure zendeskResponse
107102

@@ -183,20 +178,6 @@ extractEmailAddress ticketId = do
183178
liftIO $ appendFile "emailAddress.txt" (emailAddress <> "\n")
184179
liftIO $ putTextLn emailAddress
185180

186-
187-
-- | Process specifig ticket id (can be used for testing) only inspects the one's with logs
188-
-- TODO(ks): Switch to `(MonadReader Config m)`, pure function?
189-
getTicketAttachments :: TicketInfo -> App [Attachment]
190-
getTicketAttachments TicketInfo{..} = do
191-
192-
-- Get the function from the configuration
193-
getTicketComments <- asksZendeskLayer zlGetTicketComments
194-
comments <- getTicketComments tiId
195-
196-
-- However, if we want this to be more composable...
197-
pure $ getAttachmentsFromComment comments
198-
199-
200181
-- | A pure function for fetching @Attachment@ from @Comment@.
201182
getAttachmentsFromComment :: [Comment] -> [Attachment]
202183
getAttachmentsFromComment comments = do
@@ -219,32 +200,44 @@ getAttachmentsFromComment comments = do
219200
isAttachmentZip :: Attachment -> Bool
220201
isAttachmentZip attachment = "application/zip" == aContentType attachment
221202

203+
-- | Get zendesk responses
204+
-- | Returns with maybe because it could return no response
205+
getZendeskResponses :: [Comment] -> [Attachment] -> TicketInfo -> App (Maybe ZendeskResponse)
206+
getZendeskResponses comments attachments ticketInfo
207+
| not (null attachments) = inspectAttachments ticketInfo attachments
208+
| not (null comments) = Just <$> responseNoLogs ticketInfo
209+
| otherwise = return Nothing
210+
211+
-- | Inspect only the latest attachment. We could propagate this
212+
-- @Maybe@ upwards or use an @Either@ which will go hand in hand
213+
-- with the idea that we need to improve our exception handling.
214+
inspectAttachments :: TicketInfo -> [Attachment] -> App (Maybe ZendeskResponse)
215+
inspectAttachments ticketInfo attachments = runMaybeT $ do
216+
217+
config <- ask
218+
getAttachment <- asksZendeskLayer zlGetAttachment
222219

223-
-- | Given number of file of inspect, knowledgebase and attachment,
224-
-- analyze the logs and return the results.
225-
--
226-
-- The results are following:
227-
--
228-
-- __(comment, tags, bool of whether is should be public comment)__
229-
inspectAttachment :: TicketInfo -> Attachment -> App ZendeskResponse
230-
inspectAttachment ticketInfo@TicketInfo{..} att = do
220+
let lastAttach :: Maybe Attachment
221+
lastAttach = safeHead . reverse . sort $ attachments
231222

232-
Config{..} <- ask
223+
lastAttachment <- MaybeT . pure $ lastAttach
224+
att <- MaybeT $ getAttachment lastAttachment
233225

234-
getAttachment <- asksZendeskLayer zlGetAttachment
235-
printText <- asksIOLayer iolPrintText
226+
pure $ inspectAttachment config ticketInfo att
236227

237-
attachment <- fromMaybe (error "Missing Attachment content!") <$> getAttachment att
238228

239-
let rawLog = getAttachmentContent attachment
229+
-- | Given number of file of inspect, knowledgebase and attachment,
230+
-- analyze the logs and return the results.
231+
inspectAttachment :: Config -> TicketInfo -> AttachmentContent -> ZendeskResponse
232+
inspectAttachment Config{..} ticketInfo@TicketInfo{..} attContent = do
233+
234+
let rawLog = getAttachmentContent attContent
240235
let results = extractLogsFromZip cfgNumOfLogsToAnalyze rawLog
241236

242237
case results of
243238
Left _ -> do
244239

245-
printText . renderErrorCode $ SentLogCorrupted
246-
247-
pure ZendeskResponse
240+
ZendeskResponse
248241
{ zrTicketId = tiId
249242
, zrComment = prettyFormatLogReadError ticketInfo
250243
, zrTags = [renderErrorCode SentLogCorrupted]
@@ -259,11 +252,7 @@ inspectAttachment ticketInfo@TicketInfo{..} att = do
259252
let errorCodes = extractErrorCodes analysisResult
260253
let commentRes = prettyFormatAnalysis analysisResult ticketInfo
261254

262-
let fErrorCode = foldr (\errorCode acc -> errorCode <> ";" <> acc) "" errorCodes
263-
264-
printText fErrorCode
265-
266-
pure ZendeskResponse
255+
ZendeskResponse
267256
{ zrTicketId = tiId
268257
, zrComment = commentRes
269258
, zrTags = errorCodes
@@ -272,23 +261,34 @@ inspectAttachment ticketInfo@TicketInfo{..} att = do
272261

273262
Left _ -> do
274263

275-
printText . renderTicketStatus $ NoKnownIssue
276-
277-
pure ZendeskResponse
264+
ZendeskResponse
278265
{ zrTicketId = tiId
279266
, zrComment = prettyFormatNoIssues ticketInfo
280267
, zrTags = [renderTicketStatus NoKnownIssue]
281268
, zrIsPublic = cfgIsCommentPublic
282269
}
283270

271+
responseNoLogs :: TicketInfo -> App ZendeskResponse
272+
responseNoLogs TicketInfo{..} = do
273+
Config {..} <- ask
274+
pure ZendeskResponse
275+
{ zrTicketId = tiId
276+
, zrComment = prettyFormatNoLogs
277+
, zrTags = [renderTicketStatus NoLogAttached]
278+
, zrIsPublic = cfgIsCommentPublic
279+
}
280+
284281
-- | Filter analyzed tickets
285282
filterAnalyzedTickets :: [TicketInfo] -> [TicketInfo]
286283
filterAnalyzedTickets ticketsInfo =
287284
filter ticketsFilter ticketsInfo
288285
where
289286
ticketsFilter :: TicketInfo -> Bool
290287
ticketsFilter ticketInfo =
291-
isTicketAnalyzed ticketInfo && isTicketOpen ticketInfo && isTicketBlacklisted ticketInfo
288+
isTicketAnalyzed ticketInfo
289+
&& isTicketOpen ticketInfo
290+
&& isTicketBlacklisted ticketInfo
291+
&& isTicketInGoguenTestnet ticketInfo
292292

293293
isTicketAnalyzed :: TicketInfo -> Bool
294294
isTicketAnalyzed TicketInfo{..} = (renderTicketStatus AnalyzedByScriptV1_0) `notElem` (getTicketTags tiTags)
@@ -301,3 +301,6 @@ filterAnalyzedTickets ticketsInfo =
301301
isTicketBlacklisted :: TicketInfo -> Bool
302302
isTicketBlacklisted TicketInfo{..} = tiId `notElem` [TicketId 9377,TicketId 10815]
303303

304+
isTicketInGoguenTestnet :: TicketInfo -> Bool
305+
isTicketInGoguenTestnet TicketInfo{..} = "goguen_testnets" `notElem` getTicketTags tiTags
306+

src/LogAnalysis/Classifier.hs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ module LogAnalysis.Classifier
66
( extractErrorCodes
77
, extractIssuesFromLogs
88
, prettyFormatAnalysis
9-
, prettyFormatNoIssues
109
, prettyFormatLogReadError
10+
, prettyFormatNoIssues
11+
, prettyFormatNoLogs
1112
) where
1213

1314
import Universum
@@ -104,3 +105,41 @@ prettyFormatLogReadError ticketInfo =
104105
"We tried to analyze the log that you submitted and it appears that your log cannot be processed. Please try sending the log file once again. Please go to https://daedaluswallet.io/faq/ and see Issue 200 for instructions. Please reply to this email with when you respond." <>
105106
prettyFooter ticketInfo
106107

108+
prettyFormatNoLogs :: Text
109+
prettyFormatNoLogs =
110+
"Dear user," <> "\n\n" <>
111+
"Thank you for contacting the IOHK Technical Support Desk. We apologize for the delay in responding to you." <> "\n\n" <>
112+
"Most of the tickets we get are related to technical issues. If you have a Technical problem with Daedalus wallet please read on. If your request is NOT related to getting technical support you can IGNORE this email." <> "\n\n" <>
113+
"We have recently (May 29th) had a major update to the Daedalus software. You can see more details here https://daedaluswallet.io/release-notes/. If you are experiencing any technical difficulties please make sure you have upgraded to the latest version before submitting a request for support or submitting new logs (more on logs below)." <> "\n\n" <>
114+
"We scan our tickets to check for known issues before responding in person. If you have a technical issue but did not submit a log file we suggest that you reply to this message and attach your log file. Log files are required for helping with the majority of technical issues." <> "\n\n" <>
115+
116+
"Please provide more information so that we can diagnose your issue:" <> "\n\n" <>
117+
118+
"1. What is the Manufacturer and the Model number of your computer?" <> "\n" <>
119+
"2. What is the Type and Version of the Operation System (OS) are you using?" <> "\n" <>
120+
"3. Describe the issue you are experiencing in detail and attach screenshots if needed. Please tell us what you were doing when the error occurred." <> "\n" <>
121+
"4. When did this issue occur (Date)?" <> "\n" <>
122+
"5. Do you have any ideas how this happened?" <> "\n" <>
123+
"Please compress and attach your pub folder, it contains technical logs. There is NO sensitive data in your logs:" <> "\n\n" <>
124+
125+
"Windows" <> "\n\n" <>
126+
127+
"1. Go to" <> "\n" <>
128+
"C:\\Users'username\\AppData\\Roaming\\Daedalus\\Logs" <> "\n" <>
129+
"You can access them by typing %appdata% into Windows Explorer search bar." <> "\n" <>
130+
"2. Compress the pub folder into a Zip file." <> "\n" <>
131+
"3. Attach the compressed pub folder to your reply." <> "\n\n" <>
132+
133+
"Mac" <> "\n\n" <>
134+
135+
"1. Open Finder" <> "\n" <>
136+
"2. Go to the Menu Bar and select the 'Go' menu" <> "\n" <>
137+
"3. Select 'Go to Folder...'" <> "\n" <>
138+
"4. Enter the following path (this is only correct if you did not change the standard installation):" <> "\n" <>
139+
"~/Library/Application Support/Daedalus/Logs" <> "\n" <>
140+
"5. Right-click the pub folder and select Compress 'pub' in the shortcut menu." <> "\n" <>
141+
"6. Attach the compressed pub folder to your reply." <> "\n\n" <>
142+
143+
"Thanks," <> "\n" <>
144+
"The IOHK Technical Support Desk Team"
145+

0 commit comments

Comments
 (0)