Skip to content

Commit

Permalink
feat(error-handling): handle 409s on create and 404s on delete (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
Avantol13 authored Dec 17, 2018
1 parent 1dda8d1 commit 9eb5b61
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
20 changes: 18 additions & 2 deletions cirrus/google_cloud/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,16 @@ def create_group(self, name, email=None):
if email is None:
email = name.replace(" ", "-").lower() + "@" + config.GOOGLE_IDENTITY_DOMAIN
group = {"email": email, "name": name, "description": ""}
response = self._admin_service.groups().insert(body=group).execute()
try:
response = self._admin_service.groups().insert(body=group).execute()
except HttpError as err:
if err.resp.status == 409:
# conflict, group already exists. This is fine, don't raise an
# error, pass back group
return group

raise

return response

@backoff.on_exception(backoff.expo, Exception, **BACKOFF_SETTINGS)
Expand Down Expand Up @@ -1301,7 +1310,14 @@ def delete_group(self, group_id):
dict: JSON response from API call, which should be empty
`Google API Reference <https://developers.google.com/admin-sdk/directory/v1/reference/groups/delete>`_
"""
return self._admin_service.groups().delete(groupKey=group_id).execute()
try:
return self._admin_service.groups().delete(groupKey=group_id).execute()
except HttpError as err:
if err.resp.status == 404:
# not found, group doesn't exist. This is fine
return {}

raise

@backoff.on_exception(backoff.expo, Exception, **BACKOFF_SETTINGS)
@_require_authed_session
Expand Down
63 changes: 63 additions & 0 deletions test/test_google_cloud_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,43 @@ def test_create_group(test_cloud_manager):
)


def test_create_group_already_exists(test_cloud_manager):
"""
Test group creation calls google API with provided info and that response is returned
even if the group already exists
"""
# Setup #
new_group_name = "Test Group!"
new_group_email = "[email protected]"
group = {"email": new_group_email, "name": new_group_name, "description": ""}

response = httplib2.Response({"status": "409", "content-type": "application/json"})
http_error = HttpError(resp=response, content="")

mock_config = {
"groups.return_value.insert.return_value.execute.side_effect": http_error
}
test_cloud_manager._admin_service.configure_mock(**mock_config)

# Call #
group = test_cloud_manager.create_group(name=new_group_name, email=new_group_email)

# Test #
assert group["email"] == new_group_email
assert group["name"] == new_group_name

# check if new name and email are somewhere in the args to insert
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.insert.call_args
)
assert any(new_group_name in str(arg) for arg in args) or any(
new_group_name in str(kwarg) for kwarg in kwargs.values()
)
assert any(new_group_email in str(arg) for arg in args) or any(
new_group_email in str(kwarg) for kwarg in kwargs.values()
)


def test_get_group_members(test_cloud_manager):
"""
Test get group members calls google API with provided info and that response is returned
Expand Down Expand Up @@ -943,6 +980,32 @@ def test_delete_group(test_cloud_manager):
)


def test_delete_group_doesnt_exist(test_cloud_manager):
"""
Test that deleting a group that doesn't exist doesn't error out
"""
# Setup #
group_id = "123"
response = httplib2.Response({"status": "404", "content-type": "application/json"})
http_error = HttpError(resp=response, content="")
mock_config = {
"groups.return_value.delete.return_value.execute.side_effect": http_error
}
test_cloud_manager._admin_service.configure_mock(**mock_config)

# Call #
response = test_cloud_manager.delete_group(group_id)

# Test #
assert response == {}
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.delete.call_args
)
assert any((group_id == arg) for arg in args) or any(
(group_id == kwarg) for kwarg in kwargs.values()
)


class NewDatetime(datetime.datetime):
"A manipulable date replacement"

Expand Down

0 comments on commit 9eb5b61

Please sign in to comment.