mirror of https://github.com/zulip/zulip.git
message-type: Add support for "direct" as value for type parameter.
For endpoints with a `type` parameter to indicate whether the message is a stream or direct message, `POST /typing` and `POST /messages`, adds support for passing "direct" as the preferred value for direct messages, group and 1-on-1. Maintains support for "private" as a deprecated value to indicate direct messages. Fixes #24960.
This commit is contained in:
parent
42d9560413
commit
2c043c6242
|
@ -20,6 +20,15 @@ format used by the Zulip server that they are interacting with.
|
||||||
|
|
||||||
## Changes in Zulip 7.0
|
## Changes in Zulip 7.0
|
||||||
|
|
||||||
|
**Feature level 174**:
|
||||||
|
|
||||||
|
* [`POST /typing`](/api/set-typing-status), [`POST /messages`](/api/send-message):
|
||||||
|
Added `"direct"` as the preferred way to indicate a direct message for the
|
||||||
|
`type` parameter, deprecating the original `"private"`. While `"private"`
|
||||||
|
is still supported for direct messages, clients are encouraged to use to
|
||||||
|
the modern convention with servers that support it, because support for
|
||||||
|
`"private"` will eventually be removed.
|
||||||
|
|
||||||
**Feature level 173**:
|
**Feature level 173**:
|
||||||
|
|
||||||
* [`GET /scheduled_messages`](/api/get-scheduled-messages), [`DELETE
|
* [`GET /scheduled_messages`](/api/get-scheduled-messages), [`DELETE
|
||||||
|
|
|
@ -19,10 +19,10 @@ curl -X POST {{ api_url }}/v1/messages \
|
||||||
--data-urlencode topic=Castle \
|
--data-urlencode topic=Castle \
|
||||||
--data-urlencode 'content=I come not, friends, to steal away your hearts.'
|
--data-urlencode 'content=I come not, friends, to steal away your hearts.'
|
||||||
|
|
||||||
# For private messages
|
# For direct messages
|
||||||
curl -X POST {{ api_url }}/v1/messages \
|
curl -X POST {{ api_url }}/v1/messages \
|
||||||
-u BOT_EMAIL_ADDRESS:BOT_API_KEY \
|
-u BOT_EMAIL_ADDRESS:BOT_API_KEY \
|
||||||
--data-urlencode type=private \
|
--data-urlencode type=direct \
|
||||||
--data-urlencode 'to=[9]' \
|
--data-urlencode 'to=[9]' \
|
||||||
--data-urlencode 'content=With mirth and laughter let old wrinkles come.'
|
--data-urlencode 'content=With mirth and laughter let old wrinkles come.'
|
||||||
```
|
```
|
||||||
|
@ -38,7 +38,7 @@ the command-line, providing the message content via STDIN.
|
||||||
zulip-send --stream Denmark --subject Castle \
|
zulip-send --stream Denmark --subject Castle \
|
||||||
--user othello-bot@example.com --api-key a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5
|
--user othello-bot@example.com --api-key a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5
|
||||||
|
|
||||||
# For private messages
|
# For direct messages
|
||||||
zulip-send hamlet@example.com \
|
zulip-send hamlet@example.com \
|
||||||
--user othello-bot@example.com --api-key a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5
|
--user othello-bot@example.com --api-key a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5
|
||||||
```
|
```
|
||||||
|
|
|
@ -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 api_docs/changelog.md, as well as "**Changes**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 173
|
API_FEATURE_LEVEL = 174
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -3023,6 +3023,16 @@ class ArchivedMessage(AbstractMessage):
|
||||||
|
|
||||||
|
|
||||||
class Message(AbstractMessage):
|
class Message(AbstractMessage):
|
||||||
|
# Recipient types used when a Message object is provided to
|
||||||
|
# Zulip clients via the API.
|
||||||
|
#
|
||||||
|
# A detail worth noting:
|
||||||
|
# * "direct" was introduced in 2023 with the goal of
|
||||||
|
# deprecating the original "private" and becoming the
|
||||||
|
# preferred way to indicate a personal or huddle
|
||||||
|
# Recipient type via the API.
|
||||||
|
API_RECIPIENT_TYPES = ["direct", "private", "stream"]
|
||||||
|
|
||||||
search_tsvector = SearchVectorField(null=True)
|
search_tsvector = SearchVectorField(null=True)
|
||||||
|
|
||||||
def topic_name(self) -> str:
|
def topic_name(self) -> str:
|
||||||
|
|
|
@ -113,11 +113,11 @@ add_example("send_message", "/messages:post", 200, async (client, console) => {
|
||||||
};
|
};
|
||||||
console.log(await client.messages.send(params));
|
console.log(await client.messages.send(params));
|
||||||
|
|
||||||
// Send a private message
|
// Send a direct message
|
||||||
const user_id = 9;
|
const user_id = 9;
|
||||||
params = {
|
params = {
|
||||||
to: [user_id],
|
to: [user_id],
|
||||||
type: "private",
|
type: "direct",
|
||||||
content: "With mirth and laughter let old wrinkles come.",
|
content: "With mirth and laughter let old wrinkles come.",
|
||||||
};
|
};
|
||||||
console.log(await client.messages.send(params));
|
console.log(await client.messages.send(params));
|
||||||
|
@ -252,7 +252,8 @@ add_example("set_typing_status", "/typing:post", 200, async (client, console) =>
|
||||||
to: [user_id1, user_id2],
|
to: [user_id1, user_id2],
|
||||||
};
|
};
|
||||||
|
|
||||||
// The user has started to type in the group PM with Iago and Polonius
|
// The user has started typing in the group direct message
|
||||||
|
// with Iago and Polonius
|
||||||
console.log(await client.typing.send(typingParams));
|
console.log(await client.typing.send(typingParams));
|
||||||
// {code_example|end}
|
// {code_example|end}
|
||||||
});
|
});
|
||||||
|
|
|
@ -954,6 +954,7 @@ def send_message(client: Client) -> int:
|
||||||
"content": "I come not, friends, to steal away your hearts.",
|
"content": "I come not, friends, to steal away your hearts.",
|
||||||
}
|
}
|
||||||
result = client.send_message(request)
|
result = client.send_message(request)
|
||||||
|
|
||||||
# {code_example|end}
|
# {code_example|end}
|
||||||
|
|
||||||
validate_against_openapi_schema(result, "/messages", "post", "200")
|
validate_against_openapi_schema(result, "/messages", "post", "200")
|
||||||
|
@ -971,7 +972,7 @@ def send_message(client: Client) -> int:
|
||||||
ensure_users([10], ["hamlet"])
|
ensure_users([10], ["hamlet"])
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# Send a private message
|
# Send a direct message
|
||||||
user_id = 10
|
user_id = 10
|
||||||
request = {
|
request = {
|
||||||
"type": "private",
|
"type": "private",
|
||||||
|
@ -979,6 +980,7 @@ def send_message(client: Client) -> int:
|
||||||
"content": "With mirth and laughter let old wrinkles come.",
|
"content": "With mirth and laughter let old wrinkles come.",
|
||||||
}
|
}
|
||||||
result = client.send_message(request)
|
result = client.send_message(request)
|
||||||
|
|
||||||
# {code_example|end}
|
# {code_example|end}
|
||||||
|
|
||||||
validate_against_openapi_schema(result, "/messages", "post", "200")
|
validate_against_openapi_schema(result, "/messages", "post", "200")
|
||||||
|
@ -1294,7 +1296,8 @@ def set_typing_status(client: Client) -> None:
|
||||||
ensure_users([10, 11], ["hamlet", "iago"])
|
ensure_users([10, 11], ["hamlet", "iago"])
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# The user has started to type in the group PM with Iago and Polonius
|
# The user has started typing in the group direct message
|
||||||
|
# with Iago and Polonius
|
||||||
user_id1 = 10
|
user_id1 = 10
|
||||||
user_id2 = 11
|
user_id2 = 11
|
||||||
|
|
||||||
|
@ -1309,7 +1312,8 @@ def set_typing_status(client: Client) -> None:
|
||||||
validate_against_openapi_schema(result, "/typing", "post", "200")
|
validate_against_openapi_schema(result, "/typing", "post", "200")
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# The user has finished typing in the group PM with Iago and Polonius
|
# The user has finished typing in the group direct message
|
||||||
|
# with Iago and Polonius
|
||||||
user_id1 = 10
|
user_id1 = 10
|
||||||
user_id2 = 11
|
user_id2 = 11
|
||||||
|
|
||||||
|
@ -1324,7 +1328,8 @@ def set_typing_status(client: Client) -> None:
|
||||||
validate_against_openapi_schema(result, "/typing", "post", "200")
|
validate_against_openapi_schema(result, "/typing", "post", "200")
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# The user has started to type in topic "typing status" of stream "Denmark"
|
# The user has started to type in topic "typing status"
|
||||||
|
# of stream "Denmark"
|
||||||
stream_id = client.get_stream_id("Denmark")["stream_id"]
|
stream_id = client.get_stream_id("Denmark")["stream_id"]
|
||||||
topic = "typing status"
|
topic = "typing status"
|
||||||
|
|
||||||
|
@ -1341,7 +1346,8 @@ def set_typing_status(client: Client) -> None:
|
||||||
validate_against_openapi_schema(result, "/typing", "post", "200")
|
validate_against_openapi_schema(result, "/typing", "post", "200")
|
||||||
|
|
||||||
# {code_example|start}
|
# {code_example|start}
|
||||||
# The user has finished typing in topic "typing status" of stream "Denmark"
|
# The user has finished typing in topic "typing status"
|
||||||
|
# of stream "Denmark"
|
||||||
stream_id = client.get_stream_id("Denmark")["stream_id"]
|
stream_id = client.get_stream_id("Denmark")["stream_id"]
|
||||||
topic = "typing status"
|
topic = "typing status"
|
||||||
|
|
||||||
|
|
|
@ -5502,25 +5502,35 @@ paths:
|
||||||
summary: Send a message
|
summary: Send a message
|
||||||
tags: ["messages"]
|
tags: ["messages"]
|
||||||
description: |
|
description: |
|
||||||
Send a stream or a private message.
|
Send a [stream message](/help/streams-and-topics) or a
|
||||||
|
[direct message](/help/direct-messages).
|
||||||
parameters:
|
parameters:
|
||||||
- name: type
|
- name: type
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
The type of message to be sent. `private` for a private message and
|
The type of message to be sent.
|
||||||
`stream` for a stream message.
|
|
||||||
|
`"direct"` for a direct message and `"stream"` for a stream message.
|
||||||
|
|
||||||
|
**Changes**: In Zulip 7.0 (feature level 174), `"direct"` was added as
|
||||||
|
the preferred way to request a direct message, deprecating the original
|
||||||
|
`"private"`. While `"private"` is still supported for requesting direct
|
||||||
|
messages, clients are encouraged to use to the modern convention with
|
||||||
|
servers that support it, because support for `"private"` will eventually
|
||||||
|
be removed.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- private
|
- direct
|
||||||
- stream
|
- stream
|
||||||
example: private
|
- private
|
||||||
|
example: direct
|
||||||
required: true
|
required: true
|
||||||
- name: to
|
- name: to
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
For stream messages, either the name or integer ID of the stream. For
|
For stream messages, either the name or integer ID of the stream. For
|
||||||
private messages, either a list containing integer user IDs or a list
|
direct messages, either a list containing integer user IDs or a list
|
||||||
containing string email addresses.
|
containing string email addresses.
|
||||||
|
|
||||||
**Changes**: Support for using user/stream IDs was added in Zulip 2.0.0.
|
**Changes**: Support for using user/stream IDs was added in Zulip 2.0.0.
|
||||||
|
@ -5624,7 +5634,7 @@ paths:
|
||||||
"result": "error",
|
"result": "error",
|
||||||
}
|
}
|
||||||
description: |
|
description: |
|
||||||
A typical failed JSON response for when a private message is sent to a user
|
A typical failed JSON response for when a direct message is sent to a user
|
||||||
that does not exist:
|
that does not exist:
|
||||||
/messages/{message_id}/history:
|
/messages/{message_id}/history:
|
||||||
get:
|
get:
|
||||||
|
@ -15056,7 +15066,7 @@ paths:
|
||||||
summary: Set "typing" status
|
summary: Set "typing" status
|
||||||
tags: ["users"]
|
tags: ["users"]
|
||||||
description: |
|
description: |
|
||||||
Notify other users whether the current user is typing a message.
|
Notify other users whether the current user is [typing a message](/help/typing-notifications).
|
||||||
|
|
||||||
Clients implementing Zulip's typing notifications protocol should work as follows:
|
Clients implementing Zulip's typing notifications protocol should work as follows:
|
||||||
|
|
||||||
|
@ -15100,15 +15110,21 @@ paths:
|
||||||
description: |
|
description: |
|
||||||
Type of the message being composed.
|
Type of the message being composed.
|
||||||
|
|
||||||
**Changes**: New in Zulip 4.0 (feature level 58). Previously, typing
|
**Changes**: In Zulip 7.0 (feature level 174), `"direct"` was added
|
||||||
notifications were only for private messages.
|
as the preferred way to indicate a direct message is being composed,
|
||||||
|
becoming the default value for this parameter and deprecating the
|
||||||
|
original `"private"`.
|
||||||
|
|
||||||
|
New in Zulip 4.0 (feature level 58). Previously, typing notifications
|
||||||
|
were only for direct messages.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- private
|
- direct
|
||||||
- stream
|
- stream
|
||||||
default: private
|
- private
|
||||||
example: private
|
default: direct
|
||||||
|
example: direct
|
||||||
- name: op
|
- name: op
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
|
@ -15123,16 +15139,16 @@ paths:
|
||||||
- name: to
|
- name: to
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
For `"private"` type it is the user_ids of the recipients of the message being typed.
|
For `"direct"` type it is the user IDs of the recipients of the message
|
||||||
Send a JSON-encoded list of user_ids. (Use a list even if there is only one
|
being typed. Send a JSON-encoded list of user IDs. (Use a list even if
|
||||||
recipient.)
|
there is only one recipient.)
|
||||||
|
|
||||||
For `"stream"` type it is a single element list containing ID of stream in
|
For `"stream"` type it is a single element list containing ID of stream in
|
||||||
which the message is being typed.
|
which the message is being typed.
|
||||||
|
|
||||||
**Changes**: Support for typing notifications for stream messages
|
**Changes**: Support for typing notifications for stream messages
|
||||||
is new in Zulip 4.0 (feature level 58). Previously, typing
|
is new in Zulip 4.0 (feature level 58). Previously, typing
|
||||||
notifications were only for private messages.
|
notifications were only for direct messages.
|
||||||
|
|
||||||
Before Zulip 2.0.0, this parameter accepted only a JSON-encoded
|
Before Zulip 2.0.0, this parameter accepted only a JSON-encoded
|
||||||
list of email addresses. Support for the email address-based format was
|
list of email addresses. Support for the email address-based format was
|
||||||
|
@ -15149,10 +15165,10 @@ paths:
|
||||||
in: query
|
in: query
|
||||||
description: |
|
description: |
|
||||||
Topic to which message is being typed. Required for the `"stream"` type.
|
Topic to which message is being typed. Required for the `"stream"` type.
|
||||||
Ignored in the case of `"private"` type.
|
Ignored in the case of `"direct"` type.
|
||||||
|
|
||||||
**Changes**: New in Zulip 4.0 (feature level 58). Previously, typing
|
**Changes**: New in Zulip 4.0 (feature level 58). Previously, typing
|
||||||
notifications were only for private messages.
|
notifications were only for direct messages.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: typing notifications
|
example: typing notifications
|
||||||
|
|
|
@ -586,7 +586,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": orjson.dumps([othello.email]).decode(),
|
"to": orjson.dumps([othello.email]).decode(),
|
||||||
},
|
},
|
||||||
|
@ -605,7 +605,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": orjson.dumps([user_profile.email]).decode(),
|
"to": orjson.dumps([user_profile.email]).decode(),
|
||||||
},
|
},
|
||||||
|
@ -628,52 +628,60 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
|
|
||||||
def test_personal_message_by_id(self) -> None:
|
def test_personal_message_by_id(self) -> None:
|
||||||
"""
|
"""
|
||||||
Sending a personal message to a valid user ID is successful.
|
Sending a personal message to a valid user ID is successful
|
||||||
|
for both valid strings for `type` parameter.
|
||||||
"""
|
"""
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
result = self.client_post(
|
recipient_type_name = ["direct", "private"]
|
||||||
"/json/messages",
|
|
||||||
{
|
|
||||||
"type": "private",
|
|
||||||
"content": "Test message",
|
|
||||||
"to": orjson.dumps([self.example_user("othello").id]).decode(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assert_json_success(result)
|
|
||||||
|
|
||||||
msg = self.get_last_message()
|
for type in recipient_type_name:
|
||||||
self.assertEqual("Test message", msg.content)
|
result = self.client_post(
|
||||||
self.assertEqual(msg.recipient_id, self.example_user("othello").recipient_id)
|
"/json/messages",
|
||||||
|
{
|
||||||
|
"type": type,
|
||||||
|
"content": "Test message",
|
||||||
|
"to": orjson.dumps([self.example_user("othello").id]).decode(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
msg = self.get_last_message()
|
||||||
|
self.assertEqual("Test message", msg.content)
|
||||||
|
self.assertEqual(msg.recipient_id, self.example_user("othello").recipient_id)
|
||||||
|
|
||||||
def test_group_personal_message_by_id(self) -> None:
|
def test_group_personal_message_by_id(self) -> None:
|
||||||
"""
|
"""
|
||||||
Sending a personal message to a valid user ID is successful.
|
Sending a personal message to a valid user ID is successful
|
||||||
|
for both valid strings for `type` parameter.
|
||||||
"""
|
"""
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
result = self.client_post(
|
recipient_type_name = ["direct", "private"]
|
||||||
"/json/messages",
|
|
||||||
{
|
|
||||||
"type": "private",
|
|
||||||
"content": "Test message",
|
|
||||||
"to": orjson.dumps(
|
|
||||||
[self.example_user("othello").id, self.example_user("cordelia").id]
|
|
||||||
).decode(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assert_json_success(result)
|
|
||||||
|
|
||||||
msg = self.get_last_message()
|
for type in recipient_type_name:
|
||||||
self.assertEqual("Test message", msg.content)
|
result = self.client_post(
|
||||||
self.assertEqual(
|
"/json/messages",
|
||||||
msg.recipient_id,
|
|
||||||
get_huddle_recipient(
|
|
||||||
{
|
{
|
||||||
self.example_user("hamlet").id,
|
"type": type,
|
||||||
self.example_user("othello").id,
|
"content": "Test message",
|
||||||
self.example_user("cordelia").id,
|
"to": orjson.dumps(
|
||||||
}
|
[self.example_user("othello").id, self.example_user("cordelia").id]
|
||||||
).id,
|
).decode(),
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
msg = self.get_last_message()
|
||||||
|
self.assertEqual("Test message", msg.content)
|
||||||
|
self.assertEqual(
|
||||||
|
msg.recipient_id,
|
||||||
|
get_huddle_recipient(
|
||||||
|
{
|
||||||
|
self.example_user("hamlet").id,
|
||||||
|
self.example_user("othello").id,
|
||||||
|
self.example_user("cordelia").id,
|
||||||
|
}
|
||||||
|
).id,
|
||||||
|
)
|
||||||
|
|
||||||
def test_personal_message_copying_self(self) -> None:
|
def test_personal_message_copying_self(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -686,7 +694,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": orjson.dumps([hamlet.id, othello.id]).decode(),
|
"to": orjson.dumps([hamlet.id, othello.id]).decode(),
|
||||||
},
|
},
|
||||||
|
@ -704,7 +712,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": "nonexistent",
|
"to": "nonexistent",
|
||||||
},
|
},
|
||||||
|
@ -723,7 +731,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": orjson.dumps([othello.id]).decode(),
|
"to": orjson.dumps([othello.id]).decode(),
|
||||||
},
|
},
|
||||||
|
@ -733,7 +741,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"to": orjson.dumps([othello.id, cordelia.id]).decode(),
|
"to": orjson.dumps([othello.id, cordelia.id]).decode(),
|
||||||
},
|
},
|
||||||
|
@ -754,7 +762,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
"to": othello.email,
|
"to": othello.email,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Invalid message type")
|
self.assert_json_error(result, "Invalid type")
|
||||||
|
|
||||||
def test_empty_message(self) -> None:
|
def test_empty_message(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -764,7 +772,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
othello = self.example_user("othello")
|
othello = self.example_user("othello")
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{"type": "private", "content": " ", "to": othello.email},
|
{"type": "direct", "content": " ", "to": othello.email},
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Message must not be empty")
|
self.assert_json_error(result, "Message must not be empty")
|
||||||
|
|
||||||
|
@ -824,9 +832,9 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Invalid character in topic, at position 5!")
|
self.assert_json_error(result, "Invalid character in topic, at position 5!")
|
||||||
|
|
||||||
def test_invalid_message_type(self) -> None:
|
def test_invalid_recipient_type(self) -> None:
|
||||||
"""
|
"""
|
||||||
Messages other than the type of "private" or "stream" are considered as invalid
|
Messages other than the type of "direct", "private" or "stream" are invalid.
|
||||||
"""
|
"""
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
|
@ -838,7 +846,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
"topic": "Test topic",
|
"topic": "Test topic",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Invalid message type")
|
self.assert_json_error(result, "Invalid type")
|
||||||
|
|
||||||
def test_private_message_without_recipients(self) -> None:
|
def test_private_message_without_recipients(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -847,7 +855,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.login("hamlet")
|
self.login("hamlet")
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{"type": "private", "content": "Test content", "to": ""},
|
{"type": "direct", "content": "Test content", "to": ""},
|
||||||
)
|
)
|
||||||
self.assert_json_error(result, "Message must have recipients")
|
self.assert_json_error(result, "Message must have recipients")
|
||||||
|
|
||||||
|
@ -859,7 +867,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -879,7 +887,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -898,7 +906,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
result = self.client_post(
|
result = self.client_post(
|
||||||
"/json/messages",
|
"/json/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -916,7 +924,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -931,7 +939,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
Sending two mirrored huddles in the row return the same ID
|
Sending two mirrored huddles in the row return the same ID
|
||||||
"""
|
"""
|
||||||
msg = {
|
msg = {
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -1065,7 +1073,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
"to": self.mit_email("starnine"),
|
"to": self.mit_email("starnine"),
|
||||||
|
@ -1079,7 +1087,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "not-private",
|
"type": "stream",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -1098,7 +1106,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
self.mit_user("starnine"),
|
self.mit_user("starnine"),
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -1120,7 +1128,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
user,
|
user,
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
@ -1141,7 +1149,7 @@ class MessagePOSTTest(ZulipTestCase):
|
||||||
user,
|
user,
|
||||||
"/api/v1/messages",
|
"/api/v1/messages",
|
||||||
{
|
{
|
||||||
"type": "private",
|
"type": "direct",
|
||||||
"sender": self.mit_email("sipbtest"),
|
"sender": self.mit_email("sipbtest"),
|
||||||
"content": "Test message",
|
"content": "Test message",
|
||||||
"client": "zephyr_mirror",
|
"client": "zephyr_mirror",
|
||||||
|
|
|
@ -132,6 +132,23 @@ class TypingValidateToArgumentsTest(ZulipTestCase):
|
||||||
|
|
||||||
|
|
||||||
class TypingHappyPathTestPMs(ZulipTestCase):
|
class TypingHappyPathTestPMs(ZulipTestCase):
|
||||||
|
def test_valid_type_and_op_parameters(self) -> None:
|
||||||
|
recipient_type_name = ["direct", "private"]
|
||||||
|
operator_type = ["start", "stop"]
|
||||||
|
sender = self.example_user("hamlet")
|
||||||
|
recipient_user = self.example_user("othello")
|
||||||
|
|
||||||
|
for type in recipient_type_name:
|
||||||
|
for operator in operator_type:
|
||||||
|
params = dict(
|
||||||
|
to=orjson.dumps([recipient_user.id]).decode(),
|
||||||
|
op=operator,
|
||||||
|
type=type,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = self.api_post(sender, "/api/v1/typing", params)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
def test_start_to_single_recipient(self) -> None:
|
def test_start_to_single_recipient(self) -> None:
|
||||||
sender = self.example_user("hamlet")
|
sender = self.example_user("hamlet")
|
||||||
recipient_user = self.example_user("othello")
|
recipient_user = self.example_user("othello")
|
||||||
|
|
|
@ -24,7 +24,7 @@ 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 convert_to_UTC
|
from zerver.lib.timestamp import convert_to_UTC
|
||||||
from zerver.lib.topic import REQ_topic
|
from zerver.lib.topic import REQ_topic
|
||||||
from zerver.lib.validator import check_int, to_float
|
from zerver.lib.validator import check_int, check_string_in, to_float
|
||||||
from zerver.lib.zcommand import process_zcommands
|
from zerver.lib.zcommand import process_zcommands
|
||||||
from zerver.lib.zephyr import compute_mit_user_fullname
|
from zerver.lib.zephyr import compute_mit_user_fullname
|
||||||
from zerver.models import (
|
from zerver.models import (
|
||||||
|
@ -193,7 +193,7 @@ def handle_deferred_message(
|
||||||
def send_message_backend(
|
def send_message_backend(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
message_type_name: str = REQ("type"),
|
req_type: str = REQ("type", str_validator=check_string_in(Message.API_RECIPIENT_TYPES)),
|
||||||
req_to: Optional[str] = REQ("to", default=None),
|
req_to: Optional[str] = REQ("to", default=None),
|
||||||
req_sender: Optional[str] = REQ("sender", default=None, documentation_pending=True),
|
req_sender: Optional[str] = REQ("sender", default=None, documentation_pending=True),
|
||||||
forged_str: Optional[str] = REQ("forged", default=None, documentation_pending=True),
|
forged_str: Optional[str] = REQ("forged", default=None, documentation_pending=True),
|
||||||
|
@ -210,7 +210,12 @@ def send_message_backend(
|
||||||
tz_guess: Optional[str] = REQ("tz_guess", default=None, documentation_pending=True),
|
tz_guess: Optional[str] = REQ("tz_guess", default=None, documentation_pending=True),
|
||||||
time: Optional[float] = REQ(default=None, converter=to_float, documentation_pending=True),
|
time: Optional[float] = REQ(default=None, converter=to_float, documentation_pending=True),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
recipient_type_name = message_type_name
|
recipient_type_name = req_type
|
||||||
|
if recipient_type_name == "direct":
|
||||||
|
# For now, use "private" from Message.API_RECIPIENT_TYPES.
|
||||||
|
# TODO: Use "direct" here, as well as in events and
|
||||||
|
# message (created, schdeduled, drafts) objects/dicts.
|
||||||
|
recipient_type_name = "private"
|
||||||
|
|
||||||
# If req_to is None, then we default to an
|
# If req_to is None, then we default to an
|
||||||
# empty list of recipients.
|
# empty list of recipients.
|
||||||
|
|
|
@ -9,18 +9,17 @@ from zerver.lib.request import REQ, has_request_variables
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.streams import access_stream_by_id, access_stream_for_send_message
|
from zerver.lib.streams import access_stream_by_id, access_stream_for_send_message
|
||||||
from zerver.lib.validator import check_int, check_list, check_string_in
|
from zerver.lib.validator import check_int, check_list, check_string_in
|
||||||
from zerver.models import UserProfile
|
from zerver.models import Message, UserProfile
|
||||||
|
|
||||||
VALID_OPERATOR_TYPES = ["start", "stop"]
|
VALID_OPERATOR_TYPES = ["start", "stop"]
|
||||||
VALID_MESSAGE_TYPES = ["private", "stream"]
|
|
||||||
|
|
||||||
|
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def send_notification_backend(
|
def send_notification_backend(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
message_type: str = REQ(
|
req_type: str = REQ(
|
||||||
"type", str_validator=check_string_in(VALID_MESSAGE_TYPES), default="private"
|
"type", str_validator=check_string_in(Message.API_RECIPIENT_TYPES), default="direct"
|
||||||
),
|
),
|
||||||
operator: str = REQ("op", str_validator=check_string_in(VALID_OPERATOR_TYPES)),
|
operator: str = REQ("op", str_validator=check_string_in(VALID_OPERATOR_TYPES)),
|
||||||
notification_to: List[int] = REQ("to", json_validator=check_list(check_int)),
|
notification_to: List[int] = REQ("to", json_validator=check_list(check_int)),
|
||||||
|
@ -31,7 +30,12 @@ def send_notification_backend(
|
||||||
if to_length == 0:
|
if to_length == 0:
|
||||||
raise JsonableError(_("Empty 'to' list"))
|
raise JsonableError(_("Empty 'to' list"))
|
||||||
|
|
||||||
if message_type == "stream":
|
recipient_type_name = req_type
|
||||||
|
if recipient_type_name == "private":
|
||||||
|
# TODO: Use "direct" in typing notification events.
|
||||||
|
recipient_type_name = "direct"
|
||||||
|
|
||||||
|
if recipient_type_name == "stream":
|
||||||
if to_length > 1:
|
if to_length > 1:
|
||||||
raise JsonableError(_("Cannot send to multiple streams"))
|
raise JsonableError(_("Cannot send to multiple streams"))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue