Skip to content

Commit

Permalink
make create just call get instead (#328)
Browse files Browse the repository at this point in the history
  • Loading branch information
Juliya Smith committed Jun 7, 2021
1 parent 401cd38 commit 9dfdb2e
Show file tree
Hide file tree
Showing 16 changed files with 309 additions and 404 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The intended audience of this file is for py42 consumers -- as such, changes that don't affect
how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here.

## Unreleased

### Fixed

- Issue where `sdk.detectionlists.create_user()` would always fail because of API changes.
The method has been deprecated and now returns the response from `sdk.detectionlists.get()`.

### Added

- New custom exception `Py42UnableToCreateProfileError` that is raised when calling the
method `sdk.detectionlists.create_user()` due to the user not existing in Code42 or
is already in the process of being created on the back-end.

## 1.14.2 - 2021-05-07

### Fixed
Expand Down
14 changes: 11 additions & 3 deletions src/py42/clients/detectionlists.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from py42.exceptions import Py42BadRequestError
from py42.exceptions import Py42UnableToCreateProfileError
from py42.util import get_attribute_keys_from_class


Expand Down Expand Up @@ -40,16 +42,22 @@ def high_risk_employee(self):
return self._high_risk_employee_service

def create_user(self, username):
"""Create a detection list profile for a user.
`Rest Documentation <https://developer.code42.com/api/#operation/UserControllerV2_Create>`__
"""Deprecated. Used to create a detection list profile for a user, but now that
happens automatically. Thus, this method instead returns the response from
an API call that gets the user's profile.
Args:
username (str): The Code42 username of the user.
Returns:
:class:`py42.response.Py42Response`
"""
return self._user_profile_service.create(username)
try:
return self._user_profile_service.get(username)
except Py42BadRequestError as err:
if "Could not find user" in str(err):
raise Py42UnableToCreateProfileError(err, username)
raise

def get_user(self, username):
"""Get user details by username.
Expand Down
16 changes: 16 additions & 0 deletions src/py42/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ def __init__(self, exception, user_id, list_name):
super(Py42NotFoundError, self).__init__(exception, message)


class Py42UnableToCreateProfileError(Py42BadRequestError):
"""An error raised when trying to call the method for creating a detection-list
user when the user does not exist or is currently awaiting the profile to get
created on the back-end. Note: you are no longer able to create detection-list
profiles using the API; py42 only returns already existing profiles."""

def __init__(self, exception, username):
message = (
u"Detection-list profiles are now created automatically on the server. "
u"Unable to find a detection-list profile for '{}'. "
u"It is possibly still being created if you just recently created the "
u"Code42 user.".format(username)
)
super(Py42UnableToCreateProfileError, self).__init__(exception, message)


class Py42InvalidRuleError(Py42NotFoundError):
"""An exception raised when the observer rule ID does not exist."""

Expand Down
19 changes: 0 additions & 19 deletions src/py42/services/detectionlists/user_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,6 @@ def __init__(self, connection, user_context, user_service):
def _make_uri(self, action):
return u"{}{}".format(self._resource, action)

def create(self, username):
"""Create a detection list profile for a user.
Args:
username (str): Username of the user.
Returns:
:class:`py42.response.Py42Response`
"""
data = {
u"tenantId": self._user_context.get_current_tenant_id(),
u"userName": username,
u"notes": "",
u"riskFactors": [],
u"cloudUsernames": [],
}
uri = self._make_uri(u"/create")
return self._connection.post(uri, json=data)

def get_by_id(self, user_id):
"""Get user details by user UID.
Expand Down
6 changes: 2 additions & 4 deletions tests/clients/test_alertrules.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json

import pytest
from requests import HTTPError
from requests import Response
from tests.conftest import create_mock_error

from py42.clients.alertrules import AlertRulesClient
from py42.exceptions import Py42InternalServerError
Expand Down Expand Up @@ -48,9 +48,7 @@ def mock_alerts_service(mocker):

@pytest.fixture
def internal_server_error(mocker):
base_err = mocker.MagicMock(spec=HTTPError)
base_err.response = mocker.MagicMock(spec=Response)
return Py42InternalServerError(base_err)
return create_mock_error(Py42InternalServerError, mocker, "")


class TestAlertRulesClient(object):
Expand Down
59 changes: 58 additions & 1 deletion tests/clients/test_detectionlists.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import pytest
from tests.conftest import create_mock_error

from py42.clients.detectionlists import DetectionListsClient
from py42.clients.detectionlists import RiskTags
from py42.exceptions import Py42BadRequestError
from py42.exceptions import Py42UnableToCreateProfileError
from py42.services.detectionlists.departing_employee import DepartingEmployeeService
from py42.services.detectionlists.high_risk_employee import HighRiskEmployeeService
from py42.services.detectionlists.user_profile import DetectionListUserService
Expand Down Expand Up @@ -79,7 +82,61 @@ def test_create_user_calls_user_client_with_expected_values(
mock_high_risk_employee_service,
)
client.create_user("testusername")
mock_detection_list_user_service.create.assert_called_once_with("testusername")
mock_detection_list_user_service.get.assert_called_once_with("testusername")

def test_create_user_when_service_returns_cannot_find_user_bad_request_raises_unable_to_create_error(
self,
mocker,
mock_detection_list_user_service,
mock_departing_employee_service,
mock_high_risk_employee_service,
):
mock_err_response_content = """
{
"pop-bulletin": {
"type$": "com.code42.casemanagement.CaseMessages.InvalidUser",
"text$": "Could not find user: testusername",
"details": [],
"user": "testusername"
}
}"""
mock_detection_list_user_service.get.side_effect = create_mock_error(
Py42BadRequestError, mocker, mock_err_response_content
)

client = DetectionListsClient(
mock_detection_list_user_service,
mock_departing_employee_service,
mock_high_risk_employee_service,
)
with pytest.raises(Py42UnableToCreateProfileError) as err:
client.create_user("testusername")

assert (
str(err.value)
== "Detection-list profiles are now created automatically on the server. "
"Unable to find a detection-list profile for 'testusername'. It is "
"possibly still being created if you just recently created the Code42 "
"user."
)

def test_create_user_when_service_returns_bad_request_raises(
self,
mocker,
mock_detection_list_user_service,
mock_departing_employee_service,
mock_high_risk_employee_service,
):
mock_detection_list_user_service.get.side_effect = create_mock_error(
Py42BadRequestError, mocker, ""
)
client = DetectionListsClient(
mock_detection_list_user_service,
mock_departing_employee_service,
mock_high_risk_employee_service,
)
with pytest.raises(Py42BadRequestError):
client.create_user("testusername")

def test_get_user_calls_user_client_with_expected_values(
self,
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ def create_mock_response(mocker, text):
return Py42Response(response)


def create_mock_error(err_class, mocker, text):
mock_http_error = mocker.MagicMock(spec=HTTPError)
mock_http_error.response = create_mock_response(mocker, text)
return err_class(mock_http_error)


@pytest.fixture
def mock_post_not_found_session(mocker, mock_connection):
response = mocker.MagicMock(spec=Response)
Expand Down
10 changes: 4 additions & 6 deletions tests/services/detectionlists/test_departing_employee.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from datetime import datetime

import pytest
from requests import HTTPError
from requests import Response
from tests.conftest import create_mock_error
from tests.conftest import TENANT_ID_FROM_RESPONSE

from py42.exceptions import Py42BadRequestError
Expand Down Expand Up @@ -128,10 +127,9 @@ def test_add_when_user_already_on_list_raises_user_already_added_error(
):
def side_effect(url, json):
if "add" in url:
base_err = mocker.MagicMock(spec=HTTPError)
base_err.response = mocker.MagicMock(spec=Response)
base_err.response.text = "User already on list"
raise Py42BadRequestError(base_err)
raise create_mock_error(
Py42BadRequestError, mocker, "User already on list"
)

mock_connection.post.side_effect = side_effect
client = DepartingEmployeeService(
Expand Down
10 changes: 4 additions & 6 deletions tests/services/detectionlists/test_high_risk_employee.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
from requests import HTTPError
from requests import Response
from tests.conftest import create_mock_error

from py42.exceptions import Py42BadRequestError
from py42.exceptions import Py42NotFoundError
Expand Down Expand Up @@ -77,10 +76,9 @@ def test_add_when_user_already_on_list_raises_user_already_added_error(
):
def side_effect(url, json):
if "add" in url:
base_err = mocker.MagicMock(spec=HTTPError)
base_err.response = mocker.MagicMock(spec=Response)
base_err.response.text = "User already on list"
raise Py42BadRequestError(base_err)
raise create_mock_error(
Py42BadRequestError, mocker, "User already on list"
)

mock_connection.post.side_effect = side_effect
client = HighRiskEmployeeService(
Expand Down
48 changes: 10 additions & 38 deletions tests/services/detectionlists/test_user_profile.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import pytest
from requests import Response
from requests.exceptions import HTTPError
from tests.conftest import create_mock_error

from py42.exceptions import Py42BadRequestError
from py42.exceptions import Py42CloudAliasLimitExceededError
from py42.services.detectionlists.user_profile import DetectionListUserService
from py42.services.users import UserService


CLOUD_ALIAS_LIMIT_EXCEEDED_ERROR_MESSAGE = """{
"pop-bulletin": {
"type$": "com.code42.detectionlistmanagement.DetectionListMessages.ValidationError",
Expand All @@ -28,56 +26,30 @@ def mock_user_client(self, mock_connection, user_context, py42_response):

@pytest.fixture
def mock_get_by_id_fails(self, mocker, mock_connection):
response = mocker.MagicMock(spec=Response)
response.status_code = 400
exception = mocker.MagicMock(spec=HTTPError)
exception.response = response
mock_connection.post.side_effect = Py42BadRequestError(exception)
mock_connection.post.side_effect = create_mock_error(
Py42BadRequestError, mocker, ""
)
return mock_connection

@pytest.fixture
def mock_user_client_raises_exception(
self, mocker, mock_connection, user_context, py42_response
):
user_client = UserService(mock_connection)
response = mocker.MagicMock(spec=Response)
response.status_code = 400
exception = mocker.MagicMock(spec=HTTPError)
exception.response = response
mock_connection.post.side_effect = Py42BadRequestError(exception)
mock_connection.post.side_effect = create_mock_error(
Py42BadRequestError, mocker, ""
)
return user_client

@pytest.fixture
def mock_user_client_error_on_adding_cloud_aliases(
self, mocker, mock_connection, user_context, py42_response
):
user_client = UserService(mock_connection)
response = mocker.MagicMock(spec=Response)
response.status_code = 400
response.text = CLOUD_ALIAS_LIMIT_EXCEEDED_ERROR_MESSAGE
exception = mocker.MagicMock(spec=HTTPError)
exception.response = response
mock_connection.post.side_effect = Py42BadRequestError(exception)
return user_client

def test_create_posts_expected_data(
self, mock_connection, user_context, mock_user_client
):
detection_list_user_client = DetectionListUserService(
mock_connection, user_context, mock_user_client
)
detection_list_user_client.create("942897397520289999")

posted_data = mock_connection.post.call_args[1]["json"]
assert mock_connection.post.call_count == 1
assert mock_connection.post.call_args[0][0] == "v2/user/create"
assert (
posted_data["tenantId"] == user_context.get_current_tenant_id()
and posted_data["userName"] == "942897397520289999"
and posted_data["riskFactors"] == []
and posted_data["cloudUsernames"] == []
and posted_data["notes"] == ""
mock_connection.post.side_effect = create_mock_error(
Py42BadRequestError, mocker, CLOUD_ALIAS_LIMIT_EXCEEDED_ERROR_MESSAGE
)
return user_client

def test_get_posts_expected_data(
self, mock_connection, user_context, mock_user_client
Expand Down
15 changes: 4 additions & 11 deletions tests/services/storage/test_restore.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
from requests import HTTPError
from requests import Response
from tests.conftest import create_mock_error
from tests.conftest import TEST_ACCEPTING_GUID
from tests.conftest import TEST_BACKUP_SET_ID
from tests.conftest import TEST_DEVICE_GUID
Expand All @@ -23,15 +22,9 @@ def _create_expected_restore_groups(file):

@pytest.fixture
def mock_restore_connection_with_bad_request(mocker, mock_connection):
def side_effect(*args, **kwargs):
err = mocker.MagicMock(spec=HTTPError)
resp = mocker.MagicMock(spec=Response)
resp.text = "CREATE_FAILED"
err.response = resp
py42_error = Py42BadRequestError(err)
raise py42_error

mock_connection.post.side_effect = side_effect
mock_connection.post.side_effect = create_mock_error(
Py42BadRequestError, mocker, "CREATE_FAILED"
)
return mock_connection


Expand Down

0 comments on commit 9dfdb2e

Please sign in to comment.