api: Rename edit typing endpoint to /messages/{message_id}/typing.

This is more consistent with how other URLs work in Zulip.

Replaces `/message_edit_typing` with `/messages/{message_id}/typing`.
The `message_id` parameter, previously passed in the request body,
is now included in the URL path.
This commit is contained in:
opmkumar
2025-03-01 01:23:17 +05:30
committed by Tim Abbott
parent 61db2bc158
commit c97fd1bca5
8 changed files with 57 additions and 53 deletions

View File

@@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 10.0
**Feature level 361**
* [`POST /messages/{message_id}/typing`](/api/set-typing-status-for-message-edit):
Renamed `POST /messages/{message_id}/typing` to
`POST /message_edit_typing`, passing the one `message_id` parameter
in the URL path, for consistency with the rest of the API.
**Feature level 360**
* [`GET /messages/{message_id}`](/api/get-message), [`GET

View File

@@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
# new level means in api_docs/changelog.md, as well as "**Changes**"
# entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 360 # Last bumped for allowing access to archived channel messages on read.
API_FEATURE_LEVEL = 361
# 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

View File

@@ -51,11 +51,10 @@ function send_message_edit_typing_notification_ajax(
operation: "start" | "stop",
): void {
const data = {
message_id: JSON.stringify(message_id),
op: operation,
};
void channel.post({
url: "/json/message_edit_typing",
url: `/json/messages/${message_id}/typing`,
data,
error(xhr) {
if (xhr.readyState !== 0) {

View File

@@ -1608,43 +1608,35 @@ def set_typing_status(client: Client) -> None:
validate_against_openapi_schema(result, "/typing", "post", "200")
@openapi_test_function("/message_edit_typing:post")
def set_message_edit_typing_status(client: Client) -> None:
message = {"type": "stream", "to": "Verona", "topic": "test_topic", "content": "test content"}
response = client.send_message(message)
message_id = response["id"]
@openapi_test_function("/messages/{message_id}/typing:post")
def set_message_edit_typing_status(client: Client, message_id: int) -> None:
# {code_example|start}
# The user has started typing while editing a message
# The user has started typing while editing a message.
request = {
"op": "start",
"message_id": message_id,
}
result = client.call_endpoint(
"message_edit_typing",
f"/messages/{message_id}/typing",
method="POST",
request=request,
)
# {code_example|end}
assert_success_response(result)
validate_against_openapi_schema(result, "/message_edit_typing", "post", "200")
validate_against_openapi_schema(result, f"/messages/{message_id}/typing", "post", "200")
message = {"type": "stream", "to": "Verona", "topic": "test_topic", "content": "test content"}
response = client.send_message(message)
message_id = response["id"]
# {code_example|start}
# The user has stopped typing while editing a message.
request = {
"op": "stop",
"message_id": message_id,
}
result = client.call_endpoint(
"message_edit_typing",
f"/messages/{message_id}/typing",
method="POST",
request=request,
)
# {code_example|end}
assert_success_response(result)
validate_against_openapi_schema(result, "/message_edit_typing", "post", "200")
validate_against_openapi_schema(result, "/messages/{message_id}/typing", "post", "200")
@openapi_test_function("/realm/emoji/{emoji_name}:post")
@@ -1794,7 +1786,7 @@ def test_invalid_stream_error(client: Client) -> None:
def test_messages(client: Client, nonadmin_client: Client) -> None:
render_message(client)
message_id = send_message(client)
set_message_edit_typing_status(client)
set_message_edit_typing_status(client, message_id)
add_reaction(client, message_id)
remove_reaction(client, message_id)
update_message(client, message_id)

View File

@@ -21320,7 +21320,7 @@ paths:
description: |
An example JSON error response when the user composes a channel message
and `stream_id` is not specified:
/message_edit_typing:
/messages/{message_id}/typing:
post:
operationId: set-typing-status-for-message-edit
summary: Set "typing" status for message editing
@@ -21329,10 +21329,25 @@ paths:
Notify other users whether the current user is editing a message.
Typing notifications for editing messages follow the same protocol as
[set-typing-status](/api/set-typing-status), see that endpoint for details.
[set-typing-status](/api/set-typing-status), see that endpoint for
details.
**Changes**: New in Zulip 10.0 (feature level 351). Previously,
typing notifications were not available when editing messages.
**Changes**: Before Zulip 10.0 (feature level 361), the endpoint was
named `/message_edit_typing` with `message_id` a required parameter in
the request body. Clients are recommended to start using sending these
typing notifications starting from this feature level.
New in Zulip 10.0 (feature level 351). Previously, typing notifications were
not available when editing messages.
parameters:
- name: message_id
in: path
description: |
The target message's ID.
schema:
type: integer
example: 47
required: true
requestBody:
required: true
content:
@@ -21348,14 +21363,8 @@ paths:
- start
- stop
example: start
message_id:
description: |
Describes the message id of the message being edited.
type: integer
example: 47
required:
- op
- message_id
responses:
"200":
$ref: "#/components/responses/SimpleSuccess"

View File

@@ -41,12 +41,13 @@ class TypingValidateOperatorTest(ZulipTestCase):
def test_invalid_parameter_message_edit(self) -> None:
sender = self.example_user("hamlet")
msg_id = self.send_stream_message(
sender, "Denmark", topic_name="editing", content="before edit"
)
params = dict(
op="foo",
message_id=7,
)
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_error(result, "Invalid op")
@@ -112,10 +113,9 @@ class TypingValidateToArgumentsTest(ZulipTestCase):
)
result = self.api_post(
sender,
"/api/v1/message_edit_typing",
f"/api/v1/messages/{msg_id}/typing",
{
"op": "start",
"message_id": str(msg_id),
},
)
self.assert_json_error(result, "You don't have permission to edit this message")
@@ -569,7 +569,6 @@ class TypingHappyPathTestStreams(ZulipTestCase):
params = dict(
op="start",
message_id=msg_id,
)
with self.settings(MAX_STREAM_SIZE_FOR_TYPING_NOTIFICATIONS=5):
@@ -577,7 +576,7 @@ class TypingHappyPathTestStreams(ZulipTestCase):
self.assert_database_query_count(5),
self.capture_send_event_calls(expected_num_events=0) as events,
):
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 0)
@@ -747,13 +746,13 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
)
expected_user_ids = self.not_long_term_idle_subscriber_ids(channel_name, sender.realm)
params = dict(op="start", message_id=msg_id)
params = dict(op="start")
# Test typing events sent when `send_stream_typing_notifications` set to `True`.
self.assertTrue(sender.send_stream_typing_notifications)
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
self.assertEqual(orjson.loads(result.content)["msg"], "")
@@ -763,7 +762,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
do_deactivate_stream(channel, acting_user=sender)
# No events should be sent if stream is deactivated.
with self.capture_send_event_calls(expected_num_events=0) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_error(result, "Invalid message(s)")
self.assertEqual(events, [])
do_unarchive_stream(channel, channel_name, acting_user=sender)
@@ -773,7 +772,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
# No events should be sent now
with self.capture_send_event_calls(expected_num_events=0) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_error(
result, "User has disabled typing notifications for channel messages"
)
@@ -786,7 +785,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
aaron.save()
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
@@ -805,14 +804,13 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
params = dict(
op="start",
message_id=msg_id,
)
# Test typing events sent when `send_private_typing_notifications` set to `True`.
self.assertTrue(sender.send_private_typing_notifications)
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
@@ -825,7 +823,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
# No events should be sent now
with self.capture_send_event_calls(expected_num_events=0) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_error(result, "User has disabled typing notifications for direct messages")
self.assertEqual(events, [])
@@ -837,7 +835,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
recipient_user.save()
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
@@ -858,12 +856,11 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
params = dict(
op="start",
message_id=str(msg_id),
)
# Test typing events sent when `send_private_typing_notifications` set to `True`.
self.assertTrue(sender.send_private_typing_notifications)
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
@@ -876,7 +873,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
# No events should be sent now
with self.capture_send_event_calls(expected_num_events=0) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_error(result, "User has disabled typing notifications for direct messages")
self.assertEqual(events, [])
@@ -891,7 +888,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase):
expected_recipient_ids = {hamlet.id, cordelia.id}
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.api_post(sender, "/api/v1/message_edit_typing", params)
result = self.api_post(sender, f"/api/v1/messages/{msg_id}/typing", params)
self.assert_json_success(result)
self.assert_length(events, 1)
event_user_ids = set(events[0]["users"])

View File

@@ -2,7 +2,7 @@ from typing import Annotated, Literal
from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from pydantic import Json
from pydantic import Json, NonNegativeInt
from zerver.actions.message_edit import validate_user_can_edit_message
from zerver.actions.typing import (
@@ -16,7 +16,7 @@ from zerver.lib.message import access_message
from zerver.lib.response import json_success
from zerver.lib.streams import access_stream_by_id_for_message, access_stream_for_send_message
from zerver.lib.topic import maybe_rename_general_chat_to_empty_topic
from zerver.lib.typed_endpoint import ApiParamConfig, OptionalTopic, typed_endpoint
from zerver.lib.typed_endpoint import ApiParamConfig, OptionalTopic, PathOnly, typed_endpoint
from zerver.models import Recipient, UserProfile
from zerver.models.recipients import get_direct_message_group_user_ids
@@ -77,8 +77,8 @@ def send_message_edit_notification_backend(
request: HttpRequest,
user_profile: UserProfile,
*,
message_id: PathOnly[NonNegativeInt],
operator: Annotated[Literal["start", "stop"], ApiParamConfig("op")],
message_id: Json[int],
) -> HttpResponse:
# Technically, this endpoint doesn't modify the message, but we're
# attempting to send a typing notification that we're editing the

View File

@@ -399,7 +399,7 @@ v1_api_and_json_patterns = [
# POST sends a typing notification event to recipients
rest_path("typing", POST=send_notification_backend),
# POST sends a message edit typing notification
rest_path("message_edit_typing", POST=send_message_edit_notification_backend),
rest_path("messages/<int:message_id>/typing", POST=send_message_edit_notification_backend),
# user_uploads -> zerver.views.upload
rest_path("user_uploads", POST=upload_file_backend),
rest_path(