Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attach incident document to retro invite #483

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading