mirror of https://github.com/zulip/zulip.git
api: Add REST API endpoint for looking up a user by email address.
Add new rest api endpoint GET users/{email} for looking up a user by email, which is useful especially for corporate API applications that might already have a user's email address. Fixes #14302.
This commit is contained in:
parent
1212083218
commit
dfafdda9b3
|
@ -10,6 +10,10 @@ below features are supported.
|
|||
|
||||
## Changes in Zulip 4.0
|
||||
|
||||
**Feature level 39**
|
||||
|
||||
* Added new [GET /users/{email}](/api/get-user-by-email) endpoint.
|
||||
|
||||
**Feature level 38**
|
||||
|
||||
* [`POST /register`](/api/register-queue): Increased
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# Get a user by email
|
||||
|
||||
{generate_api_description(/users/{email}:get)}
|
||||
|
||||
## Usage examples
|
||||
|
||||
{start_tabs}
|
||||
{tab|python}
|
||||
|
||||
{generate_code_example(python)|/users/{email}:get|example}
|
||||
|
||||
{tab|curl}
|
||||
|
||||
{generate_code_example(curl, include=[""])|/users/{email}:get|example}
|
||||
|
||||
You may pass the `client_gravatar` or `include_custom_profile_fields` query parameter as follows:
|
||||
|
||||
{generate_code_example(curl)|/users/{email}:get|example}
|
||||
|
||||
{end_tabs}
|
||||
|
||||
## Parameters
|
||||
|
||||
**Note**: The following parameters are all URL query parameters.
|
||||
|
||||
{generate_api_arguments_table|zulip.yaml|/users/{email}:get}
|
||||
|
||||
## Response
|
||||
|
||||
#### Return values
|
||||
|
||||
{generate_return_values_table|zulip.yaml}|/users/{email}:get}
|
||||
|
||||
#### Example response
|
||||
|
||||
A typical successful JSON response may look like:
|
||||
|
||||
{generate_code_example|/users/{email}:get|fixture(200)}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
{generate_api_description(/users:get)}
|
||||
|
||||
You can also [fetch details on a single user](/api/get-user).
|
||||
|
||||
## Usage examples
|
||||
|
||||
{start_tabs}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
* [Get all users](/api/get-users)
|
||||
* [Get own user](/api/get-own-user)
|
||||
* [Get a user](/api/get-user)
|
||||
* [Get a user by email](/api/get-user-by-email)
|
||||
* [Update a user](/api/update-user)
|
||||
* [Create a user](/api/create-user)
|
||||
* [Deactivate a user](/api/deactivate-user)
|
||||
|
|
|
@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
|
|||
#
|
||||
# Changes should be accompanied by documentation explaining what the
|
||||
# new level means in templates/zerver/api/changelog.md.
|
||||
API_FEATURE_LEVEL = 38
|
||||
API_FEATURE_LEVEL = 39
|
||||
|
||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||
# only when going from an old version of the code to a newer version. Bump
|
||||
|
|
|
@ -222,6 +222,20 @@ def get_members(client: Client) -> None:
|
|||
assert member.get("profile_data", None) is not None
|
||||
|
||||
|
||||
@openapi_test_function("/users/{email}:get")
|
||||
def get_user_by_email(client: Client) -> None:
|
||||
|
||||
# {code_example|start}
|
||||
# Fetch details on a user given a user ID
|
||||
email = "iago@zulip.com"
|
||||
result = client.call_endpoint(
|
||||
url=f"/users/{email}",
|
||||
method="GET",
|
||||
)
|
||||
# {code_example|end}
|
||||
validate_against_openapi_schema(result, "/users/{email}", "get", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/users/{user_id}:get")
|
||||
def get_single_user(client: Client) -> None:
|
||||
|
||||
|
@ -1287,6 +1301,7 @@ def test_users(client: Client) -> None:
|
|||
deactivate_user(client)
|
||||
reactivate_user(client)
|
||||
update_user(client)
|
||||
get_user_by_email(client)
|
||||
get_subscription_status(client)
|
||||
get_profile(client)
|
||||
update_notification_settings(client)
|
||||
|
|
|
@ -4027,6 +4027,8 @@ paths:
|
|||
includes values of [custom profile field](/help/add-custom-profile-fields).
|
||||
|
||||
`GET {{ api_url }}/v1/users`
|
||||
|
||||
You can also [fetch details on a single user](/api/get-user).
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/ClientGravatar"
|
||||
- $ref: "#/components/parameters/IncludeCustomProfileFields"
|
||||
|
@ -5450,6 +5452,94 @@ paths:
|
|||
"result": "success",
|
||||
"msg": "",
|
||||
}
|
||||
/users/{email}:
|
||||
get:
|
||||
operationId: get_user_by_email
|
||||
tags: ["users"]
|
||||
description: |
|
||||
Fetch details for a single user in the organization given a Zulip display
|
||||
email address.
|
||||
|
||||
`GET {{ api_url }}/v1/users/{email}`
|
||||
|
||||
Note that this endpoint uses Zulip display emails addresses
|
||||
for organizations that have configured limited [email address
|
||||
visibility](/help/restrict-visibility-of-email-addresses).
|
||||
|
||||
You can also fetch details on [all users in the organization](/api/get-users) or
|
||||
[by user ID](/api/get-user). Fetching by user ID is generally recommended
|
||||
when possible, as users can
|
||||
[change their email address](/help/change-your-email-address).
|
||||
|
||||
*This endpoint is new in Zulip Server 4.0 (feature level 39).*
|
||||
parameters:
|
||||
- name: email
|
||||
in: path
|
||||
description: |
|
||||
The email address of the user whose details you want to fetch.
|
||||
schema:
|
||||
type: string
|
||||
example: iago@zulip.com
|
||||
required: true
|
||||
- $ref: "#/components/parameters/ClientGravatar"
|
||||
- $ref: "#/components/parameters/IncludeCustomProfileFields"
|
||||
responses:
|
||||
"200":
|
||||
description: Success.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/JsonSuccessBase"
|
||||
- additionalProperties: false
|
||||
properties:
|
||||
result: {}
|
||||
msg: {}
|
||||
user:
|
||||
$ref: "#/components/schemas/User"
|
||||
example:
|
||||
{
|
||||
"msg": "",
|
||||
"result": "success",
|
||||
"user":
|
||||
{
|
||||
"date_joined": "2019-10-20T07:50:53.729659+00:00",
|
||||
"full_name": "King Hamlet",
|
||||
"is_guest": false,
|
||||
"profile_data":
|
||||
{
|
||||
"4": {"value": "vim"},
|
||||
"2":
|
||||
{
|
||||
"value": "I am:\n* The prince of Denmark\n* Nephew to the usurping Claudius",
|
||||
"rendered_value": "<p>I am:</p>\n<ul>\n<li>The prince of Denmark</li>\n<li>Nephew to the usurping Claudius</li>\n</ul>",
|
||||
},
|
||||
"5": {"value": "1900-01-01"},
|
||||
"7": {"value": "[11]"},
|
||||
"6": {"value": "https://blog.zulig.org"},
|
||||
"1":
|
||||
{
|
||||
"value": "+0-11-23-456-7890",
|
||||
"rendered_value": "<p>+0-11-23-456-7890</p>",
|
||||
},
|
||||
"8": {"value": "zulipbot"},
|
||||
"3":
|
||||
{
|
||||
"rendered_value": "<p>Dark chocolate</p>",
|
||||
"value": "Dark chocolate",
|
||||
},
|
||||
},
|
||||
"user_id": 10,
|
||||
"is_bot": false,
|
||||
"bot_type": null,
|
||||
"timezone": "",
|
||||
"is_admin": false,
|
||||
"is_owner": false,
|
||||
"avatar_url": "https://secure.gravatar.com/avatar/6d8cad0fd00256e7b40691d27ddfd466?d=identicon&version=1",
|
||||
"is_active": true,
|
||||
"email": "hamlet@zulip.com",
|
||||
},
|
||||
}
|
||||
/users/{user_id}:
|
||||
get:
|
||||
operationId: get_user
|
||||
|
@ -5459,7 +5549,8 @@ paths:
|
|||
|
||||
`GET {{ api_url }}/v1/users/{user_id}`
|
||||
|
||||
You can also fetch details on [all users in the organization](/api/get-users).
|
||||
You can also fetch details on [all users in the organization](/api/get-users)
|
||||
or [by email](/api/get-user-by-email).
|
||||
|
||||
*This endpoint is new in Zulip Server 3.0 (feature level 1).*
|
||||
parameters:
|
||||
|
|
|
@ -1064,6 +1064,7 @@ class OpenAPIRegexTest(ZulipTestCase):
|
|||
== "/users/{user_id}/subscriptions/{stream_id}"
|
||||
)
|
||||
assert find_openapi_endpoint("/users/iago@zulip.com/presence") == "/users/{email}/presence"
|
||||
assert find_openapi_endpoint("/users/iago@zulip.com") == "/users/{email}"
|
||||
assert find_openapi_endpoint("/messages/23") == "/messages/{message_id}"
|
||||
assert find_openapi_endpoint("/realm/emoji/realm_emoji_1") == "/realm/emoji/{emoji_name}"
|
||||
|
||||
|
|
|
@ -1815,6 +1815,35 @@ class GetProfileTest(ZulipTestCase):
|
|||
self.assertEqual(result["user"]["email"], bot.email)
|
||||
self.assertTrue(result["user"]["is_bot"])
|
||||
|
||||
def test_get_user_by_email(self) -> None:
|
||||
user = self.example_user("hamlet")
|
||||
self.login("hamlet")
|
||||
result = orjson.loads(self.client_get(f"/json/users/{user.email}").content)
|
||||
|
||||
self.assertEqual(result["user"]["email"], user.email)
|
||||
|
||||
self.assertEqual(result["user"]["full_name"], user.full_name)
|
||||
self.assertIn("user_id", result["user"])
|
||||
self.assertNotIn("profile_data", result["user"])
|
||||
self.assertFalse(result["user"]["is_bot"])
|
||||
self.assertFalse(result["user"]["is_admin"])
|
||||
self.assertFalse(result["user"]["is_owner"])
|
||||
|
||||
result = orjson.loads(
|
||||
self.client_get(
|
||||
f"/json/users/{user.email}", {"include_custom_profile_fields": "true"}
|
||||
).content
|
||||
)
|
||||
self.assertIn("profile_data", result["user"])
|
||||
|
||||
result = self.client_get("/json/users/invalid")
|
||||
self.assert_json_error(result, "No such user")
|
||||
|
||||
bot = self.example_user("default_bot")
|
||||
result = orjson.loads(self.client_get(f"/json/users/{bot.email}").content)
|
||||
self.assertEqual(result["user"]["email"], bot.email)
|
||||
self.assertTrue(result["user"]["is_bot"])
|
||||
|
||||
def test_get_all_profiles_avatar_urls(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
result = self.api_get(hamlet, "/api/v1/users")
|
||||
|
|
|
@ -31,7 +31,7 @@ from zerver.lib.bot_config import set_bot_config
|
|||
from zerver.lib.email_validation import email_allowed_for_realm
|
||||
from zerver.lib.exceptions import CannotDeactivateLastUserError, OrganizationOwnerRequired
|
||||
from zerver.lib.integrations import EMBEDDED_BOTS
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.request import REQ, JsonableError, has_request_variables
|
||||
from zerver.lib.response import json_error, json_success
|
||||
from zerver.lib.streams import access_stream_by_id, access_stream_by_name, subscribed_to_stream
|
||||
from zerver.lib.types import Validator
|
||||
|
@ -75,6 +75,7 @@ from zerver.models import (
|
|||
Service,
|
||||
Stream,
|
||||
UserProfile,
|
||||
get_user,
|
||||
get_user_by_delivery_email,
|
||||
get_user_by_id_in_realm_including_cross_realm,
|
||||
get_user_including_cross_realm,
|
||||
|
@ -637,3 +638,23 @@ def get_subscription_backend(
|
|||
subscription_status = {"is_subscribed": subscribed_to_stream(target_user, stream_id)}
|
||||
|
||||
return json_success(subscription_status)
|
||||
|
||||
|
||||
@has_request_variables
|
||||
def get_user_by_email(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
email: str,
|
||||
include_custom_profile_fields: bool = REQ(validator=check_bool, default=False),
|
||||
client_gravatar: bool = REQ(validator=check_bool, default=False),
|
||||
) -> HttpResponse:
|
||||
realm = user_profile.realm
|
||||
|
||||
target_user = None
|
||||
if email is not None:
|
||||
try:
|
||||
target_user = get_user(email, realm)
|
||||
except UserProfile.DoesNotExist:
|
||||
raise JsonableError(_("No such user"))
|
||||
|
||||
return get_members_backend(request, user_profile, user_id=target_user.id)
|
||||
|
|
|
@ -199,6 +199,7 @@ from zerver.views.users import (
|
|||
get_members_backend,
|
||||
get_profile_backend,
|
||||
get_subscription_backend,
|
||||
get_user_by_email,
|
||||
patch_bot_backend,
|
||||
reactivate_user_backend,
|
||||
regenerate_bot_api_key,
|
||||
|
@ -292,6 +293,7 @@ v1_api_and_json_patterns = [
|
|||
DELETE=deactivate_user_backend,
|
||||
),
|
||||
rest_path("users/<int:user_id>/subscriptions/<int:stream_id>", GET=get_subscription_backend),
|
||||
rest_path("users/<email>", GET=get_user_by_email),
|
||||
rest_path("bots", GET=get_bots_backend, POST=add_bot_backend),
|
||||
rest_path("bots/<int:bot_id>/api_key/regenerate", POST=regenerate_bot_api_key),
|
||||
rest_path("bots/<int:bot_id>", PATCH=patch_bot_backend, DELETE=deactivate_bot_backend),
|
||||
|
|
Loading…
Reference in New Issue