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

[SYNPY-1186] When a username is a number, getUserProfile cannot retrieve the user #1014

Merged
Merged
66 changes: 66 additions & 0 deletions synapseclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,72 @@ def invalidateAPIKey(self):
if self._is_logged_in():
self.restDELETE("/secretKey", endpoint=self.authEndpoint)

@functools.lru_cache()
def get_user_profile_by_username(
self,
id: str = None,
BWMac marked this conversation as resolved.
Show resolved Hide resolved
sessionToken: str = None,
) -> UserProfile:
"""
Get the details about a Synapse user.
Retrieves information on the current user if 'id' is omitted or is empty string.
:param id: The userName of a user
:param sessionToken: The session token to use to find the user profile
:returns: The user profile for the user of interest.

Example::
my_profile = syn.get_user_name_profile()
BWMac marked this conversation as resolved.
Show resolved Hide resolved
freds_profile = syn.get_user_name_profile('fredcommo')
"""
if id:
if isinstance(id, str):
principals = self._findPrincipals(id)
for principal in principals:
if principal.get("userName", None).lower() == id.lower():
id = principal["ownerId"]
break
else:
raise ValueError(f"Can't find user '{id}'")
else:
raise TypeError("id must be a 'userName' string")
else:
id = ""
BWMac marked this conversation as resolved.
Show resolved Hide resolved
uri = f"/userProfile/{id}"
return UserProfile(
**self.restGET(
uri, headers={"sessionToken": sessionToken} if sessionToken else None
)
)

@functools.lru_cache()
def get_user_profile_by_id(
BryanFauble marked this conversation as resolved.
Show resolved Hide resolved
self,
id: int = None,
sessionToken: str = None,
) -> UserProfile:
"""
Get the details about a Synapse user.
Retrieves information on the current user if 'id' is omitted or is empty string.
:param id: The ownerId of a user
BryanFauble marked this conversation as resolved.
Show resolved Hide resolved
:param sessionToken: The session token to use to find the user profile
:returns: The user profile for the user of interest.

Example::
my_profile = syn.getUserProfile()
BWMac marked this conversation as resolved.
Show resolved Hide resolved
freds_profile = syn.getUserProfile('fredcommo')
BWMac marked this conversation as resolved.
Show resolved Hide resolved
"""
if id:
if not isinstance(id, int):
raise TypeError("id must be an 'ownerId' integer")
else:
id = ""
uri = f"/userProfile/{id}"
return UserProfile(
**self.restGET(
uri, headers={"sessionToken": sessionToken} if sessionToken else None
)
)

@functools.lru_cache()
BWMac marked this conversation as resolved.
Show resolved Hide resolved
def getUserProfile(
self,
Expand Down
100 changes: 100 additions & 0 deletions tests/unit/synapseclient/unit_test_get_user_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import pytest
from unittest.mock import patch


test_user_profile = {
"ownerId": "1234567",
"etag": "test-etag",
"firstName": "Test",
"lastName": "User",
"emails": ["[email protected]"],
"openIds": [],
"userName": "test_user",
"displayName": "Test User",
"summary": "This is a test user",
"position": "Test Position",
"location": "Test Location",
"industry": "Test Industry",
"company": "Test Company",
"profilePicureFileHandleId": "test-file-handle",
"url": "https://example.com",
"notificationSettings": {"sendEmailNotifications": False},
"createdOn": "2022-01-01T00:00:00.000Z",
}


class TestGetUserProfileByUserName:
principals = [
{
"ownerId": "7654321",
"firstName": "test",
"lastName": "person",
"userName": "abc",
"isIndividual": True,
},
{
"ownerId": "1234567",
"firstName": "test_2",
"lastName": "person_2",
"userName": "test_user",
"isIndividual": True,
},
]

@pytest.fixture(autouse=True, scope="function")
def setup_method(self, syn):
self.syn = syn
self.syn.restGET = patch.object(
self.syn, "restGET", return_value=test_user_profile
).start()
self.syn._findPrincipals = patch.object(
self.syn, "_findPrincipals", return_value=self.principals
).start()

def teardown_method(self):
self.syn.restGET.stop()
self.syn._findPrincipals.stop()

def test_that_get_user_profile_returns_expected_with_no_id(self):
result = self.syn.get_user_profile_by_username()
self.syn.restGET.assert_called_once_with("/userProfile/", headers=None)
assert result == test_user_profile

def test_that_get_user_profile_returns_expected_with_username(self):
result = self.syn.get_user_profile_by_username("test_user")
self.syn.restGET.assert_called_once_with("/userProfile/1234567", headers=None)
assert result == test_user_profile

def test_that_get_user_profile_raises_value_error_when_user_does_not_exist(self):
with pytest.raises(ValueError, match="Can't find user *"):
self.syn.get_user_profile_by_username("not_a_user")

def test_that_get_user_profile_raises_type_error_when_id_is_not_allowed_type(self):
with pytest.raises(TypeError, match="id must be a 'userName' string"):
self.syn.get_user_profile_by_username(1234567)


class TestGetUserProfileByID:
@pytest.fixture(autouse=True, scope="function")
def setup_method(self, syn):
self.syn = syn
self.syn.restGET = patch.object(
self.syn, "restGET", return_value=test_user_profile
).start()

def teardown_method(self):
self.syn.restGET.stop()

def test_that_get_user_profile_by_id_returns_expected_when_id_is_defined(self):
result = self.syn.get_user_profile_by_id(1234567)
self.syn.restGET.assert_called_once_with("/userProfile/1234567", headers=None)
assert result == test_user_profile

def test_that_get_user_profile_by_id_returns_expected_when_id_is_not_defined(self):
result = self.syn.get_user_profile_by_id()
self.syn.restGET.assert_called_once_with("/userProfile/", headers=None)
assert result == test_user_profile

def test_that_get_user_profile_by_id_raises_type_error_when_id_is_not_int(self):
with pytest.raises(TypeError, match="id must be an 'ownerId' integer"):
self.syn.get_user_profile_by_id("1234567")
Loading