Skip to content

Commit 7a1fa59

Browse files
ilaredoYuvHayun
andauthored
sendemailreply handling error (#40092)
* add dependency for core api * error handling * add rn * fix tests * fix precommit issues * Update Packs/EmailCommunication/ReleaseNotes/2_0_40.md Co-authored-by: Yuval Hayun <[email protected]> * fix execute_command will not crush if fail * fix execute_command will not crush if fail * wip * fix unit tests * keep the original structure in one of the try,except block * fix test * wip * wip * add tests when failed * wip * wip * remove return return error * add no cover for line * add no cover for main * add no cover for main * ignore ruff --------- Co-authored-by: Yuval Hayun <[email protected]>
1 parent 655b69d commit 7a1fa59

File tree

5 files changed

+129
-65
lines changed

5 files changed

+129
-65
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#### Scripts
2+
##### SendEmailReply
3+
- Improved error handling.
4+
- Updated the Docker image to: *demisto/bs4-py3:1.0.0.2129847*.

Packs/EmailCommunication/Scripts/SendEmailReply/SendEmailReply.py

Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import json
2-
import re
31
from datetime import datetime as dt
4-
52
import demistomock as demisto # noqa: F401
63
from CommonServerPython import * # noqa: F401
74
from markdown import Extension, markdown
@@ -49,16 +46,17 @@ def append_email_signature(html_body):
4946
Returns: (string) Original HTML body with HTML formatted email signature appended
5047
"""
5148
demisto.debug("append_email_signature")
52-
email_signature = demisto.executeCommand("getList", {"listName": "XSOAR - Email Communication Signature"})
53-
54-
if is_error(email_signature):
55-
demisto.debug(
56-
"Error occurred while trying to load the `XSOAR - Email Communication Signature` list. No signature added to email"
57-
)
58-
else:
49+
is_succeed, email_signature = execute_command(
50+
"getList", {"listName": "XSOAR - Email Communication Signature"}, extract_contents=False, fail_on_error=False
51+
)
52+
if is_succeed:
5953
# Find the position of the closing </html> tag and insert the signature there
6054
if re.search("(?i)</body>", html_body):
6155
html_body = re.sub("(?i)</body>", f"\r\n{email_signature[0]['Contents']}\r\n</body>", html_body)
56+
else:
57+
demisto.debug(
58+
"Error occurred while trying to load the `XSOAR - Email Communication Signature` list. No signature added to email"
59+
)
6260

6361
return html_body
6462

@@ -99,7 +97,7 @@ def validate_email_sent(
9997
Returns:
10098
str: a message which indicates that the mail was sent successfully or an error message.
10199
"""
102-
email_reply = execute_reply_mail(
100+
execute_reply_mail(
103101
incident_id,
104102
email_subject,
105103
subject_include_incident_id,
@@ -116,9 +114,6 @@ def validate_email_sent(
116114
mail_sender_instance,
117115
)
118116

119-
if is_error(email_reply):
120-
return_error(f"Error:\n {get_error(email_reply)}")
121-
122117
msg = f"Mail sent successfully. To: {email_to}"
123118
if email_cc:
124119
msg += f" Cc: {email_cc}"
@@ -153,7 +148,9 @@ def execute_reply_mail(
153148
# setting the email's subject for gmail adjustments
154149
try:
155150
demisto.debug(f"Setting incident {incident_id} email subject to {subject_with_id}")
156-
demisto.executeCommand("setIncident", {"id": incident_id, "customFields": {"emailsubject": f"{subject_with_id}"}})
151+
execute_command(
152+
"setIncident", {"id": incident_id, "customFields": {"emailsubject": f"{subject_with_id}"}}, extract_contents=False
153+
)
157154
except Exception:
158155
return_error(
159156
f"SetIncident Failed."
@@ -185,7 +182,10 @@ def execute_reply_mail(
185182
if instances.get(mail_sender_instance, {}).get("brand") == "Gmail Single User":
186183
mail_content["references"] = email_latest_message
187184
demisto.debug(f"Sending email with the following subject: {subject_with_id}, and content: {mail_content}")
188-
return demisto.executeCommand("reply-mail", mail_content)
185+
is_succeed, email_reply = execute_command("reply-mail", mail_content, extract_contents=False, fail_on_error=False)
186+
if is_succeed:
187+
return email_reply
188+
return_error(f"Error:\n {email_reply}") # noqa: RET503
189189

190190

191191
def get_email_threads(incident_id):
@@ -198,8 +198,11 @@ def get_email_threads(incident_id):
198198
"""
199199
# Get current email threads from context if any are present
200200
demisto.debug(f"Getting email threads for incident {incident_id}")
201-
incident_context = demisto.executeCommand("getContext", {"id": incident_id})
201+
is_succeed, incident_context = execute_command("getContext", {"id": incident_id}, extract_contents=False, fail_on_error=False)
202+
if not is_succeed:
203+
demisto.debug(f"Failed to retrieve email threads for incident {incident_id}")
202204
incident_email_threads = dict_safe_get(incident_context[0], ["Contents", "context", "EmailThreads"])
205+
203206
return incident_email_threads
204207

205208

@@ -330,7 +333,7 @@ def send_new_email(
330333
# Get the custom email signature, if set, and append it to the message to be sent
331334
email_html_body = append_email_signature(email_html_body)
332335

333-
email_result = send_new_mail_request(
336+
send_new_mail_request(
334337
incident_id,
335338
email_subject,
336339
subject_include_incident_id,
@@ -348,9 +351,6 @@ def send_new_email(
348351
context_html_body,
349352
)
350353

351-
if is_error(email_result):
352-
return_error(f"Error:\n {get_error(email_result)}")
353-
354354
msg = f"Mail sent successfully. To: {email_to}"
355355
if email_cc:
356356
msg += f" Cc: {email_cc}"
@@ -424,7 +424,9 @@ def send_new_mail_request(
424424
demisto.debug(
425425
f"Sending email for incident {incident_id}, with the following subject: {email_subject}, and content: {mail_content}"
426426
)
427-
email_result = demisto.executeCommand("send-mail", mail_content)
427+
is_succeed, email_result = execute_command("send-mail", mail_content, extract_contents=False, fail_on_error=False)
428+
if not is_succeed:
429+
return_error(f"Error:\n {email_result}")
428430

429431
# Store message details in context entry
430432
create_thread_context(
@@ -490,7 +492,15 @@ def get_entry_id_list(incident_id, attachments, new_email_attachments, files):
490492
attachment_name = attachment.get("name", "")
491493
file_data = create_file_data_json(attachment, field_name)
492494
demisto.debug(f"Removing attachment {attachment} from incident {incident_id}")
493-
demisto.executeCommand("core-api-post", {"uri": f"/incident/remove/{incident_id}", "body": file_data})
495+
is_succeed, _ = execute_command(
496+
"core-api-post",
497+
{"uri": f"/incident/remove/{incident_id}", "body": file_data},
498+
extract_contents=False,
499+
fail_on_error=False,
500+
)
501+
if not is_succeed:
502+
demisto.debug("Failed to remove attachment")
503+
494504
if not isinstance(files, list):
495505
files = [files]
496506
for file in files:
@@ -552,7 +562,11 @@ def get_reply_body(notes, incident_id, attachments, reputation_calc_async=False)
552562
for note in notes:
553563
note_user = note["Metadata"]["user"]
554564
demisto.debug(f"Getting user data for user {note_user} in incident {incident_id}")
555-
note_userdata = demisto.executeCommand("getUserByUsername", {"username": note_user})
565+
is_succeed, note_userdata = execute_command(
566+
"getUserByUsername", {"username": note_user}, extract_contents=False, fail_on_error=False
567+
)
568+
if not is_succeed:
569+
demisto.debug("Failed to get user data")
556570
user_fullname = dict_safe_get(note_userdata[0], ["Contents", "name"]) or "DBot"
557571
reply_body += f"{user_fullname}: \n\n{note['Contents']}\n\n"
558572

@@ -565,21 +579,27 @@ def get_reply_body(notes, incident_id, attachments, reputation_calc_async=False)
565579

566580
entry_note = json.dumps([{"Type": 1, "ContentsFormat": "html", "Contents": reply_body, "tags": ["email-thread"]}])
567581
demisto.debug(f"Adding note to incident {incident_id}")
568-
entry_tags_res = demisto.executeCommand(
569-
"addEntries", {"entries": entry_note, "id": incident_id, "reputationCalcAsync": reputation_calc_async}
570-
)
571-
demisto.debug(f"Removing note:{note.get('ID')} from incident {incident_id}")
572-
entry_note_res = demisto.executeCommand(
582+
is_succeed, entry_note_res = execute_command(
573583
"core-api-post",
574584
{
575585
"uri": "/entry/note",
576586
"body": json.dumps({"id": note.get("ID"), "version": -1, "investigationId": incident_id, "data": "false"}),
577587
},
588+
extract_contents=False,
589+
fail_on_error=False,
590+
)
591+
if not is_succeed:
592+
return_error(entry_note_res)
593+
594+
is_succeed, entry_tags_res = execute_command(
595+
"addEntries",
596+
{"entries": entry_note, "id": incident_id, "reputationCalcAsync": reputation_calc_async},
597+
extract_contents=False,
598+
fail_on_error=False,
578599
)
579-
if is_error(entry_note_res):
580-
return_error(get_error(entry_note_res))
581-
if is_error(entry_tags_res):
582-
return_error(get_error(entry_tags_res))
600+
if not is_succeed:
601+
return_error(entry_tags_res)
602+
demisto.debug(f"Removing note:{note.get('ID')} from incident {incident_id}")
583603

584604
else:
585605
return_error("Please add a note")
@@ -623,7 +643,7 @@ def get_email_recipients(email_to, email_from, service_mail, mailbox):
623643
return email_recipients
624644

625645

626-
def get_mailbox_from_incident_labels(labels):
646+
def get_mailbox_from_incident_labels(labels): # pragma: no cover
627647
"""
628648
Gets the mailbox from which the incident was fetched from the incident labels.
629649
Args:
@@ -644,8 +664,10 @@ def get_query_window():
644664
to query back for related incidents. If yes, use this value, else use the default value of 60 days.
645665
"""
646666
demisto.debug("Getting the number of days to query back for related incidents")
647-
user_defined_time = demisto.executeCommand("getList", {"listName": "XSOAR - Email Communication Days To Query"})
648-
if is_error(user_defined_time):
667+
is_succeed, user_defined_time = execute_command(
668+
"getList", {"listName": "XSOAR - Email Communication Days To Query"}, extract_contents=False, fail_on_error=False
669+
)
670+
if not is_succeed:
649671
demisto.debug(
650672
"Error occurred while trying to load the `XSOAR - Email Communication Days To Query` list. Using"
651673
" the default query time - 60 days"
@@ -681,14 +703,16 @@ def get_incident_by_query(query):
681703

682704
query += f' modified:>="{query_from_date}"'
683705
demisto.debug(f"Querying for incidents with the following query: {query}")
684-
res = demisto.executeCommand("getIncidents", {"query": query, "populateFields": "id,status"})[0]
685-
if is_error(res):
686-
return_results(ERROR_TEMPLATE.format("getIncidents", res["Contents"]))
687-
raise DemistoException(ERROR_TEMPLATE.format("getIncidents", res["Contents"]))
688-
689-
incidents_details = res["Contents"]["data"]
706+
is_succeed, res = execute_command(
707+
"getIncidents", {"query": query, "populateFields": "id,status"}, extract_contents=False, fail_on_error=False
708+
)
709+
extracted_results = res[0]
710+
if is_succeed:
711+
incidents_details = extracted_results["Contents"]["data"]
712+
return incidents_details
690713

691-
return incidents_details
714+
return_results(ERROR_TEMPLATE.format("getIncidents", extracted_results["Contents"]))
715+
raise DemistoException(ERROR_TEMPLATE.format("getIncidents", extracted_results["Contents"]))
692716

693717

694718
def get_unique_code(incident_id, max_tries=1000):
@@ -727,10 +751,14 @@ def reset_fields():
727751
Args: None
728752
"""
729753
demisto.debug("Resetting fields used to send the email message")
730-
demisto.executeCommand(
754+
is_succeed, _ = execute_command(
731755
"setIncident",
732756
{"emailnewrecipients": "", "emailnewsubject": "", "emailnewbody": "", "addcctoemail": "", "addbcctoemail": ""},
757+
extract_contents=False,
758+
fail_on_error=False,
733759
)
760+
if not is_succeed:
761+
demisto.debug("Failed to reset fields used to send the email message")
734762

735763

736764
def resend_first_contact(
@@ -846,7 +874,9 @@ def convert_internal_url_to_base64(match):
846874
str: The src attribute with the base64-encoded image.
847875
"""
848876
original_src = match.group(1)
849-
result = demisto.executeCommand("core-api-download", {"uri": original_src})
877+
is_succeed, result = execute_command("core-api-download", {"uri": original_src}, extract_contents=False, fail_on_error=False)
878+
if not is_succeed:
879+
demisto.debug(f"Failed to download image from {original_src}")
850880
with open(demisto.getFilePath(result[0]["FileID"]).get("path"), "rb") as f:
851881
base64_data_image = base64.b64encode(f.read()).decode("utf-8")
852882
image_type = handle_image_type(base64_data_image)
@@ -919,7 +949,14 @@ def single_thread_reply(
919949
# If a unique code is not set for this incident yet, generate and set it
920950
email_code = get_unique_code(incident_id)
921951
demisto.debug(f"Setting incident {incident_id} emailgeneratedcode to {email_code}")
922-
demisto.executeCommand("setIncident", {"id": incident_id, "customFields": {"emailgeneratedcode": email_code}})
952+
is_succeed, _ = execute_command(
953+
"setIncident",
954+
{"id": incident_id, "customFields": {"emailgeneratedcode": email_code}},
955+
extract_contents=False,
956+
fail_on_error=False,
957+
)
958+
if not is_succeed:
959+
demisto.debug(f"Failed to set incident {incident_id} emailgeneratedcode")
923960
try:
924961
final_email_cc = get_email_cc(email_cc, add_cc)
925962
reply_body, context_html_body, reply_html_body = get_reply_body(notes, incident_id, attachments, reputation_calc_async)
@@ -1001,12 +1038,24 @@ def multi_thread_new(
10011038
# If there are already other values in 'emailgeneratedcodes', append the new code as a comma-separated list
10021039
if email_codes:
10031040
demisto.debug(f"Setting incident {incident_id} emailgeneratedcodes to {email_codes},{thread_code}")
1004-
demisto.executeCommand(
1005-
"setIncident", {"id": incident_id, "customFields": {"emailgeneratedcodes": f"{email_codes},{thread_code}"}}
1041+
is_succeed, _ = execute_command(
1042+
"setIncident",
1043+
{"id": incident_id, "customFields": {"emailgeneratedcodes": f"{email_codes},{thread_code}"}},
1044+
extract_contents=False,
1045+
fail_on_error=False,
10061046
)
1047+
if not is_succeed:
1048+
demisto.debug(f"failed to set incident {incident_id} emailgeneratedcode to {email_codes},{thread_code}")
10071049
else:
10081050
demisto.debug(f"Setting incident {incident_id} emailgeneratedcodes to {thread_code}")
1009-
demisto.executeCommand("setIncident", {"id": incident_id, "customFields": {"emailgeneratedcodes": f"{thread_code}"}})
1051+
is_succeed, _ = execute_command(
1052+
"setIncident",
1053+
{"id": incident_id, "customFields": {"emailgeneratedcodes": f"{thread_code}"}},
1054+
extract_contents=False,
1055+
fail_on_error=False,
1056+
)
1057+
if not is_succeed:
1058+
demisto.debug(f"failed to set incident {incident_id} emailgeneratedcodes to {thread_code}")
10101059
try:
10111060
entry_id_list = get_entry_id_list(incident_id, [], new_email_attachments, files)
10121061

@@ -1329,7 +1378,7 @@ def multi_thread_reply(
13291378
return True
13301379

13311380

1332-
def main():
1381+
def main(): # pragma: no cover
13331382
try:
13341383
demisto.debug("Starting SendEmailReply script")
13351384
args = demisto.args()
@@ -1363,7 +1412,11 @@ def main():
13631412
body_type = args.get("bodyType") or args.get("body_type") or "html"
13641413
reputation_calc_async = argToBoolean(args.get("reputation_calc_async", False))
13651414
demisto.debug("Getting notes")
1366-
notes = demisto.executeCommand("getEntries", {"filter": {"categories": ["notes"]}})
1415+
is_succeed, notes = execute_command(
1416+
"getEntries", {"filter": {"categories": ["notes"]}}, extract_contents=False, fail_on_error=False
1417+
)
1418+
if not is_succeed:
1419+
demisto.debug("Failed to get notes")
13671420

13681421
if new_email_attachments:
13691422
new_attachment_names = ", ".join([attachment.get("name", "") for attachment in new_email_attachments])

Packs/EmailCommunication/Scripts/SendEmailReply/SendEmailReply.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,16 @@ subtype: python3
5353
system: true
5454
type: python
5555
fromversion: 5.0.0
56-
dockerimage: demisto/bs4-py3:1.0.0.117152
56+
dockerimage: demisto/bs4-py3:1.0.0.2129847
5757
tests:
5858
- No tests (auto formatted)
5959
contentitemexportablefields:
6060
contentitemfields:
6161
fromServerVersion: ''
62+
dependson:
63+
must:
64+
- core-api-post
65+
- core-api-download
6266
tags: []
6367
scripttarget: 0
6468
runas: DBotWeakRole

0 commit comments

Comments
 (0)