api: Add an api endpoint for GET /users/{id}

This adds a new API endpoint for querying basic data on a single other
user in the organization, reusing the existing infrastructure (and
view function!) for getting data on all users in an organization.

Fixes #12277.
This commit is contained in:
akashaviator
2020-01-02 05:09:54 +05:30
committed by Tim Abbott
parent e39840c705
commit 1ae5964ab8
4 changed files with 40 additions and 3 deletions

View File

@@ -395,15 +395,29 @@ def get_custom_profile_field_values(custom_profile_field_values:
return profiles_by_user_id return profiles_by_user_id
def get_raw_user_data(realm: Realm, acting_user: UserProfile, client_gravatar: bool, def get_raw_user_data(realm: Realm, acting_user: UserProfile, client_gravatar: bool,
target_user: Optional[UserProfile]=None,
include_custom_profile_fields: bool=True) -> Dict[int, Dict[str, str]]: include_custom_profile_fields: bool=True) -> Dict[int, Dict[str, str]]:
user_dicts = get_realm_user_dicts(realm.id) """Fetches data about the target user(s) appropriate for sending to
acting_user via the standard format for the Zulip API. If
target_user is None, we fetch all users in the realm.
"""
profiles_by_user_id = None profiles_by_user_id = None
custom_profile_field_data = None custom_profile_field_data = None
# target_user is an optional parameter which is passed when user data of a specific user
# is required. It is 'None' otherwise.
if target_user is not None:
user_dicts = [user_profile_to_user_row(target_user)]
else:
user_dicts = get_realm_user_dicts(realm.id)
if include_custom_profile_fields: if include_custom_profile_fields:
base_query = CustomProfileFieldValue.objects.select_related("field") base_query = CustomProfileFieldValue.objects.select_related("field")
# TODO: Consider optimizing this query away with caching. # TODO: Consider optimizing this query away with caching.
custom_profile_field_values = base_query.filter(user_profile__realm_id=realm.id) custom_profile_field_values = base_query.filter(user_profile__realm_id=realm.id)
if target_user is not None:
custom_profile_field_values = base_query.filter(user_profile=target_user)
else:
custom_profile_field_values = base_query.filter(user_profile__realm_id=realm.id)
profiles_by_user_id = get_custom_profile_field_values(custom_profile_field_values) profiles_by_user_id = get_custom_profile_field_values(custom_profile_field_values)
result = {} result = {}

View File

@@ -1336,6 +1336,22 @@ class GetProfileTest(ZulipTestCase):
self.assertFalse(result['is_bot']) self.assertFalse(result['is_bot'])
self.assertTrue(result['is_admin']) self.assertTrue(result['is_admin'])
# Tests the GET ../users/{id} api endpoint.
user = self.example_user('hamlet')
result = ujson.loads(self.client_get('/json/users/{}'.format(user.id)).content)
self.assertEqual(result['members'][0]['email'], user.email)
self.assertEqual(result['members'][0]['full_name'], user.full_name)
self.assertIn("user_id", result['members'][0])
self.assertNotIn("profile_data", result['members'][0])
self.assertFalse(result['members'][0]['is_bot'])
self.assertFalse(result['members'][0]['is_admin'])
result = ujson.loads(self.client_get('/json/users/{}?include_custom_profile_fields=true'.format(user.id)).content)
self.assertIn('profile_data', result['members'][0])
result = self.client_get('/json/users/{}?'.format(30))
self.assert_json_error(result, "No such user")
def test_api_get_empty_profile(self) -> None: def test_api_get_empty_profile(self) -> None:
""" """
Ensure GET /users/me returns a max message id and returns successfully Ensure GET /users/me returns a max message id and returns successfully

View File

@@ -395,7 +395,7 @@ def get_bots_backend(request: HttpRequest, user_profile: UserProfile) -> HttpRes
return json_success({'bots': list(map(bot_info, bot_profiles))}) return json_success({'bots': list(map(bot_info, bot_profiles))})
@has_request_variables @has_request_variables
def get_members_backend(request: HttpRequest, user_profile: UserProfile, def get_members_backend(request: HttpRequest, user_profile: UserProfile, user_id: Optional[int]=None,
include_custom_profile_fields: bool=REQ(validator=check_bool, include_custom_profile_fields: bool=REQ(validator=check_bool,
default=False), default=False),
client_gravatar: bool=REQ(validator=check_bool, default=False) client_gravatar: bool=REQ(validator=check_bool, default=False)
@@ -411,7 +411,13 @@ def get_members_backend(request: HttpRequest, user_profile: UserProfile,
# If email addresses are only available to administrators, # If email addresses are only available to administrators,
# clients cannot compute gravatars, so we force-set it to false. # clients cannot compute gravatars, so we force-set it to false.
client_gravatar = False client_gravatar = False
target_user = None
if user_id is not None:
target_user = access_user_by_id(user_profile, user_id, allow_deactivated=True,
read_only=True)
members = get_raw_user_data(realm, user_profile, client_gravatar=client_gravatar, members = get_raw_user_data(realm, user_profile, client_gravatar=client_gravatar,
target_user=target_user,
include_custom_profile_fields=include_custom_profile_fields) include_custom_profile_fields=include_custom_profile_fields)
return json_success({'members': members.values()}) return json_success({'members': members.values()})

View File

@@ -146,7 +146,8 @@ v1_api_and_json_patterns = [
url(r'^users/(?!me/)(?P<email>[^/]*)/presence$', rest_dispatch, url(r'^users/(?!me/)(?P<email>[^/]*)/presence$', rest_dispatch,
{'GET': 'zerver.views.presence.get_presence_backend'}), {'GET': 'zerver.views.presence.get_presence_backend'}),
url(r'^users/(?P<user_id>[0-9]+)$', rest_dispatch, url(r'^users/(?P<user_id>[0-9]+)$', rest_dispatch,
{'PATCH': 'zerver.views.users.update_user_backend', {'GET': 'zerver.views.users.get_members_backend',
'PATCH': 'zerver.views.users.update_user_backend',
'DELETE': 'zerver.views.users.deactivate_user_backend'}), 'DELETE': 'zerver.views.users.deactivate_user_backend'}),
url(r'^bots$', rest_dispatch, url(r'^bots$', rest_dispatch,
{'GET': 'zerver.views.users.get_bots_backend', {'GET': 'zerver.views.users.get_bots_backend',