mirror of https://github.com/zulip/zulip.git
api: Send full message in GET /messages/{message_id} response.
Previously, this URL just returned the `raw_content` field. It seems cleanest to just make it a single-message variant of GET /messages, deprecating the only format.
This commit is contained in:
parent
b4675d978f
commit
82837304ec
|
@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 5.0
|
## Changes in Zulip 5.0
|
||||||
|
|
||||||
|
**Feature level 120**
|
||||||
|
|
||||||
|
* [`GET /messages/{message_id}`](/api/get-message): This endpoint
|
||||||
|
now sends the full message details. Previously, it only returned
|
||||||
|
the message's raw Markdown content.
|
||||||
|
|
||||||
**Feature level 119**
|
**Feature level 119**
|
||||||
|
|
||||||
* [`POST /register`](/api/register-queue): The `unread_msgs` section
|
* [`POST /register`](/api/register-queue): The `unread_msgs` section
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
* [Add an emoji reaction](/api/add-reaction)
|
* [Add an emoji reaction](/api/add-reaction)
|
||||||
* [Remove an emoji reaction](/api/remove-reaction)
|
* [Remove an emoji reaction](/api/remove-reaction)
|
||||||
* [Render a message](/api/render-message)
|
* [Render a message](/api/render-message)
|
||||||
* [Get a message's raw Markdown](/api/get-raw-message)
|
* [Fetch a single message](/api/get-message)
|
||||||
* [Check if messages match narrow](/api/check-messages-match-narrow)
|
* [Check if messages match narrow](/api/check-messages-match-narrow)
|
||||||
* [Get a message's edit history](/api/get-message-history)
|
* [Get a message's edit history](/api/get-message-history)
|
||||||
* [Update personal message flags](/api/update-message-flags)
|
* [Update personal message flags](/api/update-message-flags)
|
||||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
|
||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in templates/zerver/api/changelog.md, as well as
|
# new level means in templates/zerver/api/changelog.md, as well as
|
||||||
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
|
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 119
|
API_FEATURE_LEVEL = 120
|
||||||
|
|
||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
# 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
|
# only when going from an old version of the code to a newer version. Bump
|
||||||
|
|
|
@ -5524,20 +5524,37 @@ paths:
|
||||||
}
|
}
|
||||||
/messages/{message_id}:
|
/messages/{message_id}:
|
||||||
get:
|
get:
|
||||||
operationId: get-raw-message
|
operationId: get-message
|
||||||
summary: Get a message's raw Markdown
|
summary: Fetch a single message.
|
||||||
tags: ["messages"]
|
tags: ["messages"]
|
||||||
description: |
|
description: |
|
||||||
Get the raw content of a message.
|
Given a message ID, return the message object.
|
||||||
|
|
||||||
`GET {{ api_url }}/v1/messages/{msg_id}`
|
`GET {{ api_url }}/v1/messages/{msg_id}`
|
||||||
|
|
||||||
This is a rarely-used endpoint relevant for clients that primarily
|
Additionally, a `raw_content` field is included. This field is
|
||||||
work with HTML-rendered messages but might need to occasionally fetch
|
useful for clients that primarily work with HTML-rendered
|
||||||
the message's raw Markdown (e.g. for pre-filling a message-editing
|
messages but might need to occasionally fetch the message's
|
||||||
UI).
|
raw Markdown (e.g. for [view
|
||||||
|
source](/help/view-the-markdown-source-of-a-message) or
|
||||||
|
prefilling a message edit textarea).
|
||||||
|
|
||||||
|
**Changes**: Before Zulip 5.0 (feature level 120), this
|
||||||
|
endpoint only returned the `raw_content` field.
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/components/parameters/MessageId"
|
- $ref: "#/components/parameters/MessageId"
|
||||||
|
- name: apply_markdown
|
||||||
|
in: query
|
||||||
|
description: |
|
||||||
|
If `true`, message content is returned in the rendered HTML
|
||||||
|
format. If `false`, message content is returned in the raw
|
||||||
|
Markdown-format text that user entered.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 5.0 (feature level 120).
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
example: false
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Success.
|
description: Success.
|
||||||
|
@ -5553,13 +5570,99 @@ paths:
|
||||||
msg: {}
|
msg: {}
|
||||||
raw_content:
|
raw_content:
|
||||||
type: string
|
type: string
|
||||||
|
deprecated: true
|
||||||
description: |
|
description: |
|
||||||
The raw content of the message.
|
The raw Markdown content of the message.
|
||||||
|
|
||||||
|
**Deprecated** and to be removed once no longer required for
|
||||||
|
legacy clients. Modern clients should prefer passing
|
||||||
|
`apply_markdown=false` to request raw message content.
|
||||||
|
message:
|
||||||
|
description: |
|
||||||
|
An object containing details of the message.
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 5.0 (feature level 120).
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/components/schemas/MessagesBase"
|
||||||
|
- additionalProperties: false
|
||||||
|
properties:
|
||||||
|
avatar_url:
|
||||||
|
nullable: true
|
||||||
|
client: {}
|
||||||
|
content: {}
|
||||||
|
content_type: {}
|
||||||
|
display_recipient: {}
|
||||||
|
edit_history: {}
|
||||||
|
id: {}
|
||||||
|
is_me_message: {}
|
||||||
|
last_edit_timestamp: {}
|
||||||
|
reactions: {}
|
||||||
|
recipient_id: {}
|
||||||
|
sender_email: {}
|
||||||
|
sender_full_name: {}
|
||||||
|
sender_id: {}
|
||||||
|
sender_realm_str: {}
|
||||||
|
stream_id: {}
|
||||||
|
subject: {}
|
||||||
|
submessages: {}
|
||||||
|
timestamp: {}
|
||||||
|
topic_links: {}
|
||||||
|
type: {}
|
||||||
|
flags:
|
||||||
|
type: array
|
||||||
|
description: |
|
||||||
|
The user's [message flags][message-flags] for the message.
|
||||||
|
|
||||||
|
[message-flags]: /api/update-message-flags#available-flags
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
example:
|
example:
|
||||||
{
|
{
|
||||||
"raw_content": "**Don't** forget your towel!",
|
"raw_content": "**Don't** forget your towel!",
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"msg": "",
|
"msg": "",
|
||||||
|
"message":
|
||||||
|
{
|
||||||
|
"subject": "",
|
||||||
|
"sender_realm_str": "zulip",
|
||||||
|
"type": "private",
|
||||||
|
"content": "<p>Security experts agree that relational algorithms are an interesting new topic in the field of networking, and scholars concur.</p>",
|
||||||
|
"flags": ["read"],
|
||||||
|
"id": 16,
|
||||||
|
"display_recipient":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"is_mirror_dummy": false,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"full_name": "King Hamlet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"is_mirror_dummy": false,
|
||||||
|
"email": "iago@zulip.com",
|
||||||
|
"full_name": "Iago",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"is_mirror_dummy": false,
|
||||||
|
"email": "prospero@zulip.com",
|
||||||
|
"full_name": "Prospero from The Tempest",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"content_type": "text/html",
|
||||||
|
"is_me_message": false,
|
||||||
|
"timestamp": 1527921326,
|
||||||
|
"sender_id": 4,
|
||||||
|
"sender_full_name": "King Hamlet",
|
||||||
|
"recipient_id": 27,
|
||||||
|
"topic_links": [],
|
||||||
|
"client": "populate_db",
|
||||||
|
"avatar_url": "https://secure.gravatar.com/avatar/6d8cad0fd00256e7b40691d27ddfd466?d=identicon&version=1",
|
||||||
|
"submessages": [],
|
||||||
|
"sender_email": "hamlet@zulip.com",
|
||||||
|
"reactions": [],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
"400":
|
"400":
|
||||||
description: Bad request.
|
description: Bad request.
|
||||||
|
|
|
@ -304,18 +304,57 @@ class EditMessageTest(EditMessageTestCase):
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
self.check_topic(msg_id, topic_name="edited")
|
self.check_topic(msg_id, topic_name="edited")
|
||||||
|
|
||||||
def test_fetch_raw_message(self) -> None:
|
def test_fetch_message_from_id(self) -> None:
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
msg_id = self.send_personal_message(
|
msg_id = self.send_personal_message(
|
||||||
from_user=self.example_user("hamlet"),
|
from_user=self.example_user("hamlet"),
|
||||||
to_user=self.example_user("cordelia"),
|
to_user=self.example_user("cordelia"),
|
||||||
content="**before** edit",
|
content="Personal message",
|
||||||
)
|
)
|
||||||
result = self.client_get(f"/json/messages/{msg_id}")
|
result = self.client_get("/json/messages/" + str(msg_id))
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
self.assertEqual(result.json()["raw_content"], "**before** edit")
|
self.assertEqual(result.json()["raw_content"], "Personal message")
|
||||||
|
self.assertEqual(result.json()["message"]["id"], msg_id)
|
||||||
|
|
||||||
|
# Send message to web public stream where hamlet is not subscribed.
|
||||||
|
# This will test case of user having no `UserMessage` but having access
|
||||||
|
# to message.
|
||||||
|
web_public_stream = self.make_stream("web-public-stream", is_web_public=True)
|
||||||
|
self.subscribe(self.example_user("cordelia"), web_public_stream.name)
|
||||||
|
web_public_stream_msg_id = self.send_stream_message(
|
||||||
|
self.example_user("cordelia"), web_public_stream.name, content="web-public message"
|
||||||
|
)
|
||||||
|
result = self.client_get("/json/messages/" + str(web_public_stream_msg_id))
|
||||||
|
self.assert_json_success(result)
|
||||||
|
self.assertEqual(result.json()["raw_content"], "web-public message")
|
||||||
|
self.assertEqual(result.json()["message"]["id"], web_public_stream_msg_id)
|
||||||
|
|
||||||
|
# Spectator should be able to fetch message in web public stream.
|
||||||
|
self.logout()
|
||||||
|
result = self.client_get("/json/messages/" + str(web_public_stream_msg_id))
|
||||||
|
self.assert_json_success(result)
|
||||||
|
self.assertEqual(result.json()["raw_content"], "web-public message")
|
||||||
|
self.assertEqual(result.json()["message"]["id"], web_public_stream_msg_id)
|
||||||
|
|
||||||
|
# Verify default is apply_markdown=True
|
||||||
|
self.assertEqual(result.json()["message"]["content"], "<p>web-public message</p>")
|
||||||
|
|
||||||
|
# Verify apply_markdown=False works correctly.
|
||||||
|
result = self.client_get(
|
||||||
|
"/json/messages/" + str(web_public_stream_msg_id), {"apply_markdown": "false"}
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
self.assertEqual(result.json()["raw_content"], "web-public message")
|
||||||
|
self.assertEqual(result.json()["message"]["content"], "web-public message")
|
||||||
|
|
||||||
|
with self.settings(WEB_PUBLIC_STREAMS_ENABLED=False):
|
||||||
|
result = self.client_get("/json/messages/" + str(web_public_stream_msg_id))
|
||||||
|
self.assert_json_error(
|
||||||
|
result, "Not logged in: API authentication or user session required", status_code=401
|
||||||
|
)
|
||||||
|
|
||||||
# Test error cases
|
# Test error cases
|
||||||
|
self.login("hamlet")
|
||||||
result = self.client_get("/json/messages/999999")
|
result = self.client_get("/json/messages/999999")
|
||||||
self.assert_json_error(result, "Invalid message(s)")
|
self.assert_json_error(result, "Invalid message(s)")
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ from zerver.context_processors import get_valid_realm_from_request
|
||||||
from zerver.lib.actions import check_update_message, do_delete_messages
|
from zerver.lib.actions import check_update_message, do_delete_messages
|
||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.html_diff import highlight_html_differences
|
from zerver.lib.html_diff import highlight_html_differences
|
||||||
from zerver.lib.message import access_message, access_web_public_message
|
from zerver.lib.message import access_message, access_web_public_message, messages_for_ids
|
||||||
from zerver.lib.request import REQ, RequestNotes, has_request_variables
|
from zerver.lib.request import REQ, RequestNotes, has_request_variables
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.timestamp import datetime_to_timestamp
|
from zerver.lib.timestamp import datetime_to_timestamp
|
||||||
|
@ -190,6 +190,7 @@ def json_fetch_raw_message(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
maybe_user_profile: Union[UserProfile, AnonymousUser],
|
maybe_user_profile: Union[UserProfile, AnonymousUser],
|
||||||
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
message_id: int = REQ(converter=to_non_negative_int, path_only=True),
|
||||||
|
apply_markdown: bool = REQ(json_validator=check_bool, default=True),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
|
|
||||||
if not maybe_user_profile.is_authenticated:
|
if not maybe_user_profile.is_authenticated:
|
||||||
|
@ -198,4 +199,30 @@ def json_fetch_raw_message(
|
||||||
else:
|
else:
|
||||||
(message, user_message) = access_message(maybe_user_profile, message_id)
|
(message, user_message) = access_message(maybe_user_profile, message_id)
|
||||||
|
|
||||||
return json_success(request, data={"raw_content": message.content})
|
flags = ["read"]
|
||||||
|
if not maybe_user_profile.is_authenticated:
|
||||||
|
allow_edit_history = realm.allow_edit_history
|
||||||
|
else:
|
||||||
|
if user_message:
|
||||||
|
flags = user_message.flags_list()
|
||||||
|
allow_edit_history = maybe_user_profile.realm.allow_edit_history
|
||||||
|
|
||||||
|
# Security note: It's important that we call this only with a
|
||||||
|
# message already fetched via `access_message` type methods,
|
||||||
|
# as we do above.
|
||||||
|
message_dict_list = messages_for_ids(
|
||||||
|
message_ids=[message.id],
|
||||||
|
user_message_flags={message_id: flags},
|
||||||
|
search_fields={},
|
||||||
|
apply_markdown=apply_markdown,
|
||||||
|
client_gravatar=True,
|
||||||
|
allow_edit_history=allow_edit_history,
|
||||||
|
)
|
||||||
|
response = dict(
|
||||||
|
message=message_dict_list[0],
|
||||||
|
# raw_content is deprecated; we will need to wait until
|
||||||
|
# clients have been fully migrated to using the modern API
|
||||||
|
# before removing this, probably in 2023.
|
||||||
|
raw_content=message.content,
|
||||||
|
)
|
||||||
|
return json_success(request, response)
|
||||||
|
|
Loading…
Reference in New Issue