mirror of https://github.com/zulip/zulip.git
api: Support user_id in get_user_presence_endpoint.
This is part of our general process of replacing emails, which are not static with time, with user_ids when referring to users in the API. We still keep the `email` reference option, since it can be useful for linking third-party applications to Zulip on an intranet that might have a user's corporate email handy and not want to do the extra round trip to lookup the user. The name of the parameter, user_id_or_email, was chosen to to make it clear that the default/preferred option is user_id. Fixes #14304.
This commit is contained in:
parent
dc67870e0c
commit
55de66f944
|
@ -10,6 +10,11 @@ below features are supported.
|
|||
|
||||
## Changes in Zulip 4.0
|
||||
|
||||
**Feature level 43**
|
||||
|
||||
* [`GET /users/{user_id_or_email}/presence`]: Added support for
|
||||
passing the `user_id` to identify the target user.
|
||||
|
||||
**Feature level 42**
|
||||
|
||||
* `PATCH /settings/display`: Added a new `default_view` setting allowing
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
# Get user presence
|
||||
|
||||
{generate_api_description(/users/{email}/presence:get)}
|
||||
{generate_api_description(/users/{user_id_or_email}/presence:get)}
|
||||
|
||||
## Usage examples
|
||||
|
||||
{start_tabs}
|
||||
{tab|python}
|
||||
|
||||
{generate_code_example(python)|/users/{email}/presence:get|example}
|
||||
{generate_code_example(python)|/users/{user_id_or_email}/presence:get|example}
|
||||
|
||||
{tab|curl}
|
||||
|
||||
{generate_code_example(curl)|/users/{email}/presence:get|example}
|
||||
{generate_code_example(curl)|/users/{user_id_or_email}/presence:get|example}
|
||||
|
||||
{end_tabs}
|
||||
|
||||
## Parameters
|
||||
|
||||
{generate_api_arguments_table|zulip.yaml|/users/{email}/presence:get}
|
||||
{generate_api_arguments_table|zulip.yaml|/users/{user_id_or_email}/presence:get}
|
||||
|
||||
## Response
|
||||
|
||||
#### Return values
|
||||
|
||||
{generate_return_values_table|zulip.yaml|/users/{email}/presence:get}
|
||||
{generate_return_values_table|zulip.yaml|/users/{user_id_or_email}/presence:get}
|
||||
|
||||
#### Example response
|
||||
|
||||
A typical successful JSON response may look like:
|
||||
|
||||
{generate_code_example|/users/{email}/presence:get|fixture(200)}
|
||||
{generate_code_example|/users/{user_id_or_email}/presence:get|fixture(200)}
|
||||
|
|
|
@ -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 = 42
|
||||
API_FEATURE_LEVEL = 43
|
||||
|
||||
# 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
|
||||
|
|
|
@ -219,7 +219,7 @@ def delete_event_queue() -> Dict[str, object]:
|
|||
}
|
||||
|
||||
|
||||
@openapi_param_value_generator(["/users/{email}/presence:get"])
|
||||
@openapi_param_value_generator(["/users/{user_id_or_email}/presence:get"])
|
||||
def get_user_presence() -> Dict[str, object]:
|
||||
iago = helpers.example_user("iago")
|
||||
client = Client.objects.create(name="curl-test-client-3")
|
||||
|
|
|
@ -140,7 +140,7 @@ def test_authorization_errors_fatal(client: Client, nonadmin_client: Client) ->
|
|||
validate_against_openapi_schema(result, "/users/me/subscriptions", "post", "400_1")
|
||||
|
||||
|
||||
@openapi_test_function("/users/{email}/presence:get")
|
||||
@openapi_test_function("/users/{user_id_or_email}/presence:get")
|
||||
def get_user_presence(client: Client) -> None:
|
||||
|
||||
# {code_example|start}
|
||||
|
@ -148,7 +148,7 @@ def get_user_presence(client: Client) -> None:
|
|||
result = client.get_user_presence("iago@zulip.com")
|
||||
# {code_example|end}
|
||||
|
||||
validate_against_openapi_schema(result, "/users/{email}/presence", "get", "200")
|
||||
validate_against_openapi_schema(result, "/users/{user_id_or_email}/presence", "get", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/users/me/presence:post")
|
||||
|
|
|
@ -4260,7 +4260,7 @@ paths:
|
|||
- $ref: "#/components/schemas/JsonSuccess"
|
||||
- example: {"msg": "", "result": "success"}
|
||||
|
||||
/users/{email}/presence:
|
||||
/users/{user_id_or_email}/presence:
|
||||
get:
|
||||
operationId: get_user_presence
|
||||
tags: ["users"]
|
||||
|
@ -4273,16 +4273,19 @@ paths:
|
|||
presence endpoint, which returns data for all active users in the
|
||||
organization, instead.
|
||||
|
||||
`GET {{ api_url }}/v1/users/{email}/presence`
|
||||
`GET {{ api_url }}/v1/users/{user_id_or_email}/presence`
|
||||
|
||||
See
|
||||
[Zulip's developer documentation](https://zulip.readthedocs.io/en/latest/subsystems/presence.html)
|
||||
for details on the data model for presence in Zulip.
|
||||
parameters:
|
||||
- name: email
|
||||
- name: user_id_or_email
|
||||
in: path
|
||||
description: |
|
||||
The email address of the user whose presence you want to fetch.
|
||||
The user_id or Zulip display email address of the user whose presence you want to fetch.
|
||||
|
||||
**Changes**: New in Zulip 4.0 (feature level 43). Previous versions only supported
|
||||
identifying the user by Zulip display email.
|
||||
schema:
|
||||
type: string
|
||||
example: iago@zulip.com
|
||||
|
|
|
@ -1063,7 +1063,10 @@ class OpenAPIRegexTest(ZulipTestCase):
|
|||
find_openapi_endpoint("/users/23/subscriptions/21")
|
||||
== "/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/presence")
|
||||
== "/users/{user_id_or_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}"
|
||||
|
|
|
@ -464,10 +464,17 @@ class SingleUserPresenceTests(ZulipTestCase):
|
|||
result = self.client_get("/json/users/cordelia@zulip.com/presence")
|
||||
self.assert_json_error(result, "No presence data for cordelia@zulip.com")
|
||||
|
||||
cordelia = self.example_user("cordelia")
|
||||
result = self.client_get(f"/json/users/{cordelia.id}/presence")
|
||||
self.assert_json_error(result, f"No presence data for {cordelia.id}")
|
||||
|
||||
do_deactivate_user(self.example_user("cordelia"))
|
||||
result = self.client_get("/json/users/cordelia@zulip.com/presence")
|
||||
self.assert_json_error(result, "No such user")
|
||||
|
||||
result = self.client_get(f"/json/users/{cordelia.id}/presence")
|
||||
self.assert_json_error(result, "No such user")
|
||||
|
||||
result = self.client_get("/json/users/default-bot@zulip.com/presence")
|
||||
self.assert_json_error(result, "Presence is not supported for bot users.")
|
||||
|
||||
|
@ -476,6 +483,10 @@ class SingleUserPresenceTests(ZulipTestCase):
|
|||
result = self.client_get("/json/users/othello@zulip.com/presence", subdomain="zephyr")
|
||||
self.assert_json_error(result, "No such user")
|
||||
|
||||
othello = self.example_user("othello")
|
||||
result = self.client_get(f"/json/users/{othello.id}/presence", subdomain="zephyr")
|
||||
self.assert_json_error(result, "No such user")
|
||||
|
||||
# Then, we check everything works
|
||||
self.login("hamlet")
|
||||
result = self.client_get("/json/users/othello@zulip.com/presence")
|
||||
|
@ -485,6 +496,13 @@ class SingleUserPresenceTests(ZulipTestCase):
|
|||
)
|
||||
self.assertEqual(set(result_dict["presence"]["website"].keys()), {"status", "timestamp"})
|
||||
|
||||
result = self.client_get(f"/json/users/{othello.id}/presence")
|
||||
result_dict = result.json()
|
||||
self.assertEqual(
|
||||
set(result_dict["presence"].keys()), {"ZulipAndroid", "website", "aggregated"}
|
||||
)
|
||||
self.assertEqual(set(result_dict["presence"]["website"].keys()), {"status", "timestamp"})
|
||||
|
||||
def test_ping_only(self) -> None:
|
||||
|
||||
self.login("othello")
|
||||
|
|
|
@ -13,25 +13,40 @@ from zerver.lib.request import REQ, JsonableError, has_request_variables
|
|||
from zerver.lib.response import json_error, json_success
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.validator import check_bool, check_capped_string
|
||||
from zerver.models import UserActivity, UserPresence, UserProfile, get_active_user
|
||||
from zerver.models import (
|
||||
UserActivity,
|
||||
UserPresence,
|
||||
UserProfile,
|
||||
get_active_user,
|
||||
get_active_user_profile_by_id_in_realm,
|
||||
)
|
||||
|
||||
|
||||
def get_presence_backend(
|
||||
request: HttpRequest, user_profile: UserProfile, email: str
|
||||
request: HttpRequest, user_profile: UserProfile, user_id_or_email: str
|
||||
) -> HttpResponse:
|
||||
# This isn't used by the webapp; it's available for API use by
|
||||
# bots and other clients. We may want to add slim_presence
|
||||
# support for it (or just migrate its API wholesale) later.
|
||||
|
||||
try:
|
||||
target = get_active_user(email, user_profile.realm)
|
||||
try:
|
||||
user_id = int(user_id_or_email)
|
||||
target = get_active_user_profile_by_id_in_realm(user_id, user_profile.realm)
|
||||
except ValueError:
|
||||
email = user_id_or_email
|
||||
target = get_active_user(email, user_profile.realm)
|
||||
except UserProfile.DoesNotExist:
|
||||
return json_error(_("No such user"))
|
||||
|
||||
if target.is_bot:
|
||||
return json_error(_("Presence is not supported for bot users."))
|
||||
|
||||
presence_dict = get_presence_for_user(target.id)
|
||||
if len(presence_dict) == 0:
|
||||
return json_error(_("No presence data for {email}").format(email=email))
|
||||
return json_error(
|
||||
_("No presence data for {user_id_or_email}").format(user_id_or_email=user_id_or_email)
|
||||
)
|
||||
|
||||
# For initial version, we just include the status and timestamp keys
|
||||
result = dict(presence=presence_dict[target.email])
|
||||
|
|
|
@ -371,7 +371,7 @@ v1_api_and_json_patterns = [
|
|||
# It's important that this sit after users/me/presence so that
|
||||
# Django's URL resolution order doesn't break the
|
||||
# /users/me/presence endpoint.
|
||||
rest_path("users/<email>/presence", GET=get_presence_backend),
|
||||
rest_path("users/<user_id_or_email>/presence", GET=get_presence_backend),
|
||||
rest_path("realm/presence", GET=get_statuses_for_realm),
|
||||
rest_path("users/me/status", POST=update_user_status_backend),
|
||||
# user_groups -> zerver.views.user_groups
|
||||
|
|
Loading…
Reference in New Issue