Skip to content

Commit

Permalink
Attach incident document to retro invite (#483)
Browse files Browse the repository at this point in the history
* Adding functionality to attach the incident document to the meeting invite

* Formatting
  • Loading branch information
sylviamclaughlin authored Apr 26, 2024
1 parent 0e1cbd0 commit fd02214
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 11 deletions.
18 changes: 17 additions & 1 deletion app/integrations/google_workspace/google_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get_freebusy(time_min, time_max, items, **kwargs):


@handle_google_api_errors
def insert_event(start, end, emails, title, **kwargs):
def insert_event(start, end, emails, title, incident_document, **kwargs):
"""Creates a new event in the specified calendars.
Args:
Expand All @@ -71,6 +71,21 @@ def insert_event(start, end, emails, title, **kwargs):
"summary": title,
"guestsCanModify": True,
}
if incident_document:
body["attachments"] = [
{
"fileUrl": f"https://docs.google.com/document/d/{incident_document}",
"mimeType": "application/vnd.google-apps.document",
"title": "Incident Document",
}
]
else:
# Optionally handle the case where 'incident_document' is None or empty
# For example, remove 'attachments' from 'body' if it shouldn't exist without a valid document
body.pop(
"attachments", None
) # This removes 'attachments' if it exists, does nothing if it doesn't

body.update({convert_string_to_camel_case(k): v for k, v in kwargs.items()})
if "delegated_user_email" in kwargs and kwargs["delegated_user_email"] is not None:
delegated_user_email = kwargs["delegated_user_email"]
Expand All @@ -86,6 +101,7 @@ def insert_event(start, end, emails, title, **kwargs):
delegated_user_email=delegated_user_email,
body=body,
calendarId="primary",
supportsAttachments=True,
)
return result.get("htmlLink")

Expand Down
24 changes: 23 additions & 1 deletion app/modules/incident/incident_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,30 @@ def schedule_incident_retro(client, body, ack):
if "bot_id" not in response:
user_emails.append(response["email"])

# get the incident document
# get and update the incident document
document_id = ""
response = client.bookmarks_list(channel_id=channel_id)
if response["ok"]:
for item in range(len(response["bookmarks"])):
if response["bookmarks"][item]["title"] == "Incident report":
document_id = google_docs.extract_google_doc_id(
response["bookmarks"][item]["link"]
)
else:
logging.warning(
"No bookmark link for the incident document found for channel %s",
channel_name,
)

# convert the data to string so that we can send it as private metadata
data_to_send = json.dumps({"emails": user_emails, "topic": channel_topic})
data_to_send = json.dumps(
{
"emails": user_emails,
"topic": channel_topic,
"incident_document": document_id,
}
)

blocks = {
"type": "modal",
Expand Down
4 changes: 4 additions & 0 deletions app/modules/incident/schedule_retro.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def schedule_event(event_details, days):
email = email.strip()
items.append({"id": email})

# get the incident document link
incident_document = json.loads(event_details).get("incident_document")

# Execute the query to find all the busy times for all the participants
freebusy_result = get_freebusy(time_min, time_max, items)

Expand Down Expand Up @@ -62,6 +65,7 @@ def schedule_event(event_details, days):
first_available_end.isoformat(),
emails,
"Retro " + incident_name,
incident_document,
**event_config,
)

Expand Down
76 changes: 73 additions & 3 deletions app/tests/integrations/google_workspace/test_google_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ def test_insert_event_no_kwargs_no_delegated_email(
end = start
emails = ["[email protected]", "[email protected]"]
title = "Test Event"
result = google_calendar.insert_event(start, end, emails, title)
document_id = "test_document_id"
result = google_calendar.insert_event(start, end, emails, title, document_id)
assert result == "test_link"
mock_execute_google_api_call.assert_called_once_with(
"calendar",
Expand All @@ -169,8 +170,16 @@ def test_insert_event_no_kwargs_no_delegated_email(
"attendees": [{"email": email.strip()} for email in emails],
"summary": title,
"guestsCanModify": True,
"attachments": [
{
"fileUrl": f"https://docs.google.com/document/d/{document_id}",
"mimeType": "application/vnd.google-apps.document",
"title": "Incident Document",
}
],
},
calendarId="primary",
supportsAttachments=True,
)
assert not mock_convert_string_to_camel_case.called
assert mock_os_environ_get.called_once_with("SRE_BOT_EMAIL")
Expand All @@ -190,13 +199,72 @@ def test_insert_event_with_kwargs(
end = start
emails = ["[email protected]", "[email protected]"]
title = "Test Event"
document_id = "test_document_id"
kwargs = {
"location": "Test Location",
"description": "Test Description",
"delegated_user_email": "test_custom_email",
"time_zone": "Magic/Time_Zone",
"attachments": [
{
"fileUrl": "https://docs.google.com/document/d/test_document_id",
"mimeType": "application/vnd.google-apps.document",
"title": "Incident Document",
}
],
}
result = google_calendar.insert_event(
start, end, emails, title, document_id, **kwargs
)
assert result == "test_link"
mock_execute_google_api_call.assert_called_once_with(
"calendar",
"v3",
"events",
"insert",
scopes=["https://www.googleapis.com/auth/calendar.events"],
delegated_user_email="test_custom_email",
body={
"start": {"dateTime": start, "timeZone": "Magic/Time_Zone"},
"end": {"dateTime": end, "timeZone": "Magic/Time_Zone"},
"attendees": [{"email": email.strip()} for email in emails],
"summary": title,
"guestsCanModify": True,
**kwargs,
},
calendarId="primary",
supportsAttachments=True,
)
for key in kwargs:
mock_convert_string_to_camel_case.assert_any_call(key)

assert not mock_os_environ_get.called


@patch("os.environ.get", return_value="test_email")
@patch("integrations.google_workspace.google_calendar.execute_google_api_call")
@patch("integrations.google_workspace.google_calendar.convert_string_to_camel_case")
def test_insert_event_with_no_document(
mock_convert_string_to_camel_case, mock_execute_google_api_call, mock_os_environ_get
):
mock_execute_google_api_call.return_value = {"htmlLink": "test_link"}
mock_convert_string_to_camel_case.side_effect = (
lambda x: x
) # just return the same value
start = datetime.now()
end = start
emails = ["[email protected]", "[email protected]"]
title = "Test Event"
document_id = ""
kwargs = {
"location": "Test Location",
"description": "Test Description",
"delegated_user_email": "test_custom_email",
"time_zone": "Magic/Time_Zone",
}
result = google_calendar.insert_event(start, end, emails, title, **kwargs)
result = google_calendar.insert_event(
start, end, emails, title, document_id, **kwargs
)
assert result == "test_link"
mock_execute_google_api_call.assert_called_once_with(
"calendar",
Expand All @@ -214,6 +282,7 @@ def test_insert_event_with_kwargs(
**kwargs,
},
calendarId="primary",
supportsAttachments=True,
)
for key in kwargs:
mock_convert_string_to_camel_case.assert_any_call(key)
Expand All @@ -237,7 +306,8 @@ def test_insert_event_api_call_error(
end = start
emails = ["[email protected]", "[email protected]"]
title = "Test Event"
google_calendar.insert_event(start, end, emails, title)
document_id = "test_document_id"
google_calendar.insert_event(start, end, emails, title, document_id)
assert (
"An unexpected error occurred in function 'insert_event': API call error"
in caplog.text
Expand Down
90 changes: 84 additions & 6 deletions app/tests/modules/incident/test_incident_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,15 @@ def test_schedule_incident_retro_successful_no_bots():
{"user": {"profile": {"email": "[email protected]"}}},
{"user": {"profile": {"email": "[email protected]"}}},
]
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}

body = {
"channel_id": "C1234567890",
Expand Down Expand Up @@ -828,7 +837,11 @@ def test_schedule_incident_retro_successful_no_bots():

# Verify the modal payload contains the correct data
expected_data = json.dumps(
{"emails": ["[email protected]", "[email protected]"], "topic": "Retro Topic"}
{
"emails": ["[email protected]", "[email protected]"],
"topic": "Retro Topic",
"incident_document": "dummy_document_id",
}
)
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand All @@ -852,6 +865,15 @@ def test_schedule_incident_retro_successful_bots():
"user": {"profile": {"email": "[email protected]", "bot_id": "B12345"}}
}, # This simulates a bot user
]
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}

body = {
"channel_id": "C1234567890",
Expand Down Expand Up @@ -879,7 +901,11 @@ def test_schedule_incident_retro_successful_bots():

# Verify the modal payload contains the correct data
expected_data = json.dumps(
{"emails": ["[email protected]", "[email protected]"], "topic": "Retro Topic"}
{
"emails": ["[email protected]", "[email protected]"],
"topic": "Retro Topic",
"incident_document": "dummy_document_id",
}
)
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand All @@ -902,6 +928,15 @@ def test_schedule_incident_retro_successful_security_group():
"user": {"profile": {"email": "[email protected]", "bot_id": "B12345"}}
}, # This simulates a bot user
]
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}

body = {
"channel_id": "C1234567890",
Expand Down Expand Up @@ -929,7 +964,11 @@ def test_schedule_incident_retro_successful_security_group():

# Verify the modal payload contains the correct data
expected_data = json.dumps(
{"emails": ["[email protected]"], "topic": "Retro Topic"}
{
"emails": ["[email protected]"],
"topic": "Retro Topic",
"incident_document": "dummy_document_id",
}
)
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand All @@ -953,6 +992,15 @@ def test_schedule_incident_retro_successful_no_security_group():
"user": {"profile": {"email": "[email protected]", "bot_id": "B12345"}}
}, # This simulates a bot user
]
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}

body = {
"channel_id": "C1234567890",
Expand Down Expand Up @@ -980,7 +1028,11 @@ def test_schedule_incident_retro_successful_no_security_group():

# Verify the modal payload contains the correct data
expected_data = json.dumps(
{"emails": ["[email protected]", "[email protected]"], "topic": "Retro Topic"}
{
"emails": ["[email protected]", "[email protected]"],
"topic": "Retro Topic",
"incident_document": "dummy_document_id",
}
)
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand All @@ -995,6 +1047,15 @@ def test_schedule_incident_retro_with_no_users():
"channel": {"topic": {"value": "Retro Topic"}}
}
mock_client.users_info.side_effect = []
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}

# Adjust the mock to simulate no users in the channel
mock_client.conversations_members.return_value = {"members": []}
Expand All @@ -1009,7 +1070,9 @@ def test_schedule_incident_retro_with_no_users():
incident_helper.schedule_incident_retro(mock_client, body, mock_ack)

# construct the expected data object
expected_data = json.dumps({"emails": [], "topic": "Retro Topic"})
expected_data = json.dumps(
{"emails": [], "topic": "Retro Topic", "incident_document": "dummy_document_id"}
)
# Assertions to validate behavior when no users are present in the channel
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand All @@ -1021,6 +1084,15 @@ def test_schedule_incident_retro_with_no_topic():
mock_ack = MagicMock()
mock_client.usergroups_users_list.return_value = {"users": ["U444444"]}
mock_client.conversations_info.return_value = {"channel": {"topic": {"value": ""}}}
mock_client.bookmarks_list.return_value = {
"ok": True,
"bookmarks": [
{
"title": "Incident report",
"link": "https://docs.google.com/document/d/dummy_document_id/edit",
}
],
}
mock_client.users_info.side_effect = []

# Adjust the mock to simulate no users in the channel
Expand All @@ -1036,7 +1108,13 @@ def test_schedule_incident_retro_with_no_topic():
incident_helper.schedule_incident_retro(mock_client, body, mock_ack)

# construct the expected data object and set the topic to a default one
expected_data = json.dumps({"emails": [], "topic": "Incident Retro"})
expected_data = json.dumps(
{
"emails": [],
"topic": "Incident Retro",
"incident_document": "dummy_document_id",
}
)
# Assertions to validate behavior when no users are present in the channel
assert (
mock_client.views_open.call_args[1]["view"]["private_metadata"] == expected_data
Expand Down
2 changes: 2 additions & 0 deletions app/tests/modules/incident/test_schedule_retro.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def test_schedule_event_successful(
event_details_dict = json.loads(event_details)
emails = event_details_dict["emails"]
topic = event_details_dict["topic"]
document_id = event_details_dict.get("incident_document")

# Call the function under test
event_link = schedule_retro.schedule_event(event_details, mock_days)
Expand All @@ -82,6 +83,7 @@ def test_schedule_event_successful(
find_first_available_slot_mock.return_value[1].isoformat(),
emails,
"Retro " + topic,
document_id,
description="This is a retro meeting to discuss incident: " + topic,
conferenceData={
"createRequest": {
Expand Down

0 comments on commit fd02214

Please sign in to comment.