api: Fix encoding of strings in streams endpoint.

* Remove unnecessary json_validator for string parameters.
* Update frontend to pass right parameter.

Bump api feature level and highlight the fix for `emojiset`
parameter of `settings/display` endpoint in zulip.yaml file.

Fixes part of #18035.
This commit is contained in:
Gaurav Pandey 2021-05-08 22:11:54 +05:30 committed by Tim Abbott
parent 9abe1cafbe
commit 96e035a2f0
6 changed files with 62 additions and 72 deletions

View File

@ -652,7 +652,7 @@ export function change_stream_name(e) {
channel.patch({ channel.patch({
// Stream names might contain unsafe characters so we must encode it first. // Stream names might contain unsafe characters so we must encode it first.
url: "/json/streams/" + stream_id, url: "/json/streams/" + stream_id,
data: {new_name: JSON.stringify(new_name)}, data: {new_name},
success() { success() {
new_name_box.val(""); new_name_box.val("");
ui_report.success( ui_report.success(
@ -705,7 +705,7 @@ export function change_stream_description(e) {
// Description might contain unsafe characters so we must encode it first. // Description might contain unsafe characters so we must encode it first.
url: "/json/streams/" + stream_id, url: "/json/streams/" + stream_id,
data: { data: {
description: JSON.stringify(description), description,
}, },
success() { success() {
// The event from the server will update the rest of the UI // The event from the server will update the rest of the UI

View File

@ -10,6 +10,13 @@ below features are supported.
## Changes in Zulip 4.0 ## Changes in Zulip 4.0
**Feature level 64**
* `PATCH /streams/{stream_id}`: Removed unnecessary JSON-encoding of string
parameters `new_name` and `description`.
* `PATCH /settings/display`: Removed unnecessary JSON-encoding of string
parameters `default_view`, `emojiset` and `timezone`.
**Feature level 63** **Feature level 63**
* `PATCH /settings/notifications`: Removed unnecessary JSON-encoding of string * `PATCH /settings/notifications`: Removed unnecessary JSON-encoding of string

View File

@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
# #
# 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. # new level means in templates/zerver/api/changelog.md.
API_FEATURE_LEVEL = 63 API_FEATURE_LEVEL = 64
# 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

View File

@ -9323,11 +9323,11 @@ paths:
in: query in: query
description: | description: |
Notification sound name. Notification sound name.
content:
application/json: **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 63).
schema: schema:
type: string type: string
example: ding example: ding
- name: enable_desktop_notifications - name: enable_desktop_notifications
in: query in: query
description: | description: |
@ -9598,12 +9598,12 @@ paths:
This controls both the Zulip UI as well as email notifications sent to the user. This controls both the Zulip UI as well as email notifications sent to the user.
The value needs to be a standard language code that the Zulip server has The value needs to be a standard language code that the Zulip server has
translation data for; for example, `"en"` for English` or `"de"` for German. translation data for; for example, `"en"` for English or `"de"` for German.
content:
application/json: **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 63).
schema: schema:
type: string type: string
example: "en" example: en
- name: default_view - name: default_view
in: query in: query
description: | description: |
@ -9612,9 +9612,11 @@ paths:
* "recent_topics" - Recent topics view * "recent_topics" - Recent topics view
* "all_messages" - All messages view * "all_messages" - All messages view
**Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64).
schema: schema:
type: string type: string
example: '"all_messages"' example: all_messages
- name: left_side_userlist - name: left_side_userlist
in: query in: query
description: | description: |
@ -9634,6 +9636,8 @@ paths:
* "google-blob" - Google classic * "google-blob" - Google classic
* "twitter" - Twitter * "twitter" - Twitter
* "text" - Plain text * "text" - Plain text
**Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64).
schema: schema:
type: string type: string
example: "google" example: "google"
@ -9661,11 +9665,11 @@ paths:
Timezone values supported by the server are served at Timezone values supported by the server are served at
[/static/generated/timezones.json](/static/generated/timezones.json). [/static/generated/timezones.json](/static/generated/timezones.json).
content:
application/json: **Changes**: Removed unnecessary JSON-encoding of parameter in Zulip 4.0 (feature level 64).
schema: schema:
type: string type: string
example: "Asia/Kolkata" example: "Asia/Kolkata"
responses: responses:
"200": "200":
description: Success description: Success
@ -9950,22 +9954,25 @@ paths:
- name: description - name: description
in: query in: query
description: | description: |
The new description for the stream. The new description for the stream. Limited Zulip markdown is allowed in this
content: field.
application/json:
schema: **Changes**: Removed unnecessary JSON-encoding of this parameter in
type: string Zulip 4.0 (feature level 64).
example: This stream is related to football dicsussions. schema:
type: string
example: "Discuss Italian history and travel destinations."
required: false required: false
- name: new_name - name: new_name
in: query in: query
description: | description: |
The new name for the stream. The new name for the stream.
content:
application/json: **Changes**: Removed unnecessary JSON-encoding of this parameter in
schema: Zulip 4.0 (feature level 64).
type: string schema:
example: Italy type: string
example: Italy
required: false required: false
- name: is_private - name: is_private
in: query in: query

View File

@ -759,31 +759,21 @@ class StreamAdminTest(ZulipTestCase):
stream = self.subscribe(user_profile, "stream_name1") stream = self.subscribe(user_profile, "stream_name1")
do_change_user_role(user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None) do_change_user_role(user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream.id}", {"new_name": "stream_name1"})
f"/json/streams/{stream.id}", {"new_name": orjson.dumps("stream_name1").decode()}
)
self.assert_json_error(result, "Stream already has that name!") self.assert_json_error(result, "Stream already has that name!")
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream.id}", {"new_name": "Denmark"})
f"/json/streams/{stream.id}", {"new_name": orjson.dumps("Denmark").decode()}
)
self.assert_json_error(result, "Stream name 'Denmark' is already taken.") self.assert_json_error(result, "Stream name 'Denmark' is already taken.")
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream.id}", {"new_name": "denmark "})
f"/json/streams/{stream.id}", {"new_name": orjson.dumps("denmark ").decode()}
)
self.assert_json_error(result, "Stream name 'denmark' is already taken.") self.assert_json_error(result, "Stream name 'denmark' is already taken.")
# Do a rename that is case-only--this should succeed. # Do a rename that is case-only--this should succeed.
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream.id}", {"new_name": "sTREAm_name1"})
f"/json/streams/{stream.id}", {"new_name": orjson.dumps("sTREAm_name1").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
events: List[Mapping[str, Any]] = [] events: List[Mapping[str, Any]] = []
with tornado_redirected_to_list(events): with tornado_redirected_to_list(events):
stream_id = get_stream("stream_name1", user_profile.realm).id stream_id = get_stream("stream_name1", user_profile.realm).id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "stream_name2"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("stream_name2").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
event = events[1]["event"] event = events[1]["event"]
self.assertEqual( self.assertEqual(
@ -813,9 +803,7 @@ class StreamAdminTest(ZulipTestCase):
# *NOTE: Here encoding is needed when Unicode string is passed as an argument* # *NOTE: Here encoding is needed when Unicode string is passed as an argument*
with tornado_redirected_to_list(events): with tornado_redirected_to_list(events):
stream_id = stream_name2_exists.id stream_id = stream_name2_exists.id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "नया नाम"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("नया नाम").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
# While querying, system can handle Unicode strings. # While querying, system can handle Unicode strings.
stream_name_uni_exists = get_stream("नया नाम", realm) stream_name_uni_exists = get_stream("नया नाम", realm)
@ -828,7 +816,7 @@ class StreamAdminTest(ZulipTestCase):
stream_id = stream_name_uni_exists.id stream_id = stream_name_uni_exists.id
result = self.client_patch( result = self.client_patch(
f"/json/streams/{stream_id}", f"/json/streams/{stream_id}",
{"new_name": orjson.dumps("नाम में क्या रक्खा हे").decode()}, {"new_name": "नाम में क्या रक्खा हे"},
) )
self.assert_json_success(result) self.assert_json_success(result)
# While querying, system can handle Unicode strings. # While querying, system can handle Unicode strings.
@ -840,9 +828,7 @@ class StreamAdminTest(ZulipTestCase):
# Test case to change name from one language to other. # Test case to change name from one language to other.
with tornado_redirected_to_list(events): with tornado_redirected_to_list(events):
stream_id = stream_name_new_uni_exists.id stream_id = stream_name_new_uni_exists.id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "français"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("français").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
stream_name_fr_exists = get_stream("français", realm) stream_name_fr_exists = get_stream("français", realm)
self.assertTrue(stream_name_fr_exists) self.assertTrue(stream_name_fr_exists)
@ -850,9 +836,7 @@ class StreamAdminTest(ZulipTestCase):
# Test case to change name to mixed language name. # Test case to change name to mixed language name.
with tornado_redirected_to_list(events): with tornado_redirected_to_list(events):
stream_id = stream_name_fr_exists.id stream_id = stream_name_fr_exists.id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "français name"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("français name").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
stream_name_mixed_exists = get_stream("français name", realm) stream_name_mixed_exists = get_stream("français name", realm)
self.assertTrue(stream_name_mixed_exists) self.assertTrue(stream_name_mixed_exists)
@ -867,7 +851,7 @@ class StreamAdminTest(ZulipTestCase):
stream_id = get_stream("stream_private_name1", realm).id stream_id = get_stream("stream_private_name1", realm).id
result = self.client_patch( result = self.client_patch(
f"/json/streams/{stream_id}", f"/json/streams/{stream_id}",
{"new_name": orjson.dumps("stream_private_name2").decode()}, {"new_name": "stream_private_name2"},
) )
self.assert_json_success(result) self.assert_json_success(result)
notified_user_ids = set(events[1]["users"]) notified_user_ids = set(events[1]["users"])
@ -896,7 +880,7 @@ class StreamAdminTest(ZulipTestCase):
with tornado_redirected_to_list(events): with tornado_redirected_to_list(events):
result = self.client_patch( result = self.client_patch(
f"/json/streams/{new_stream.id}", f"/json/streams/{new_stream.id}",
{"new_name": orjson.dumps("stream_rename").decode()}, {"new_name": "stream_rename"},
) )
self.assert_json_success(result) self.assert_json_success(result)
self.assertEqual(len(events), 3) self.assertEqual(len(events), 3)
@ -914,9 +898,7 @@ class StreamAdminTest(ZulipTestCase):
self.assertFalse(sub.is_stream_admin) self.assertFalse(sub.is_stream_admin)
stream_id = get_stream("stream_name1", user_profile.realm).id stream_id = get_stream("stream_name1", user_profile.realm).id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "stream_name2"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("stream_name2").decode()}
)
self.assert_json_error(result, "Must be an organization or stream administrator") self.assert_json_error(result, "Must be an organization or stream administrator")
def test_notify_on_stream_rename(self) -> None: def test_notify_on_stream_rename(self) -> None:
@ -926,9 +908,7 @@ class StreamAdminTest(ZulipTestCase):
stream = self.subscribe(user_profile, "stream_name1") stream = self.subscribe(user_profile, "stream_name1")
do_change_user_role(user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None) do_change_user_role(user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream.id}", {"new_name": "stream_name2"})
f"/json/streams/{stream.id}", {"new_name": orjson.dumps("stream_name2").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
# Inspect the notification message sent # Inspect the notification message sent
@ -956,9 +936,7 @@ class StreamAdminTest(ZulipTestCase):
self.assert_json_success(result) self.assert_json_success(result)
stream_id = get_stream("private_stream", iago.realm).id stream_id = get_stream("private_stream", iago.realm).id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "new_private_stream"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("new_private_stream").decode()}
)
self.assert_json_success(result) self.assert_json_success(result)
result = self.client_patch( result = self.client_patch(
@ -992,9 +970,7 @@ class StreamAdminTest(ZulipTestCase):
stream_id = get_stream("private_stream_1", hamlet.realm).id stream_id = get_stream("private_stream_1", hamlet.realm).id
result = self.client_patch( result = self.client_patch(f"/json/streams/{stream_id}", {"new_name": "private_stream_2"})
f"/json/streams/{stream_id}", {"new_name": orjson.dumps("private_stream_2").decode()}
)
self.assert_json_error(result, "Invalid stream id") self.assert_json_error(result, "Invalid stream id")
result = self.client_patch( result = self.client_patch(

View File

@ -244,7 +244,7 @@ def update_stream_backend(
user_profile: UserProfile, user_profile: UserProfile,
stream_id: int, stream_id: int,
description: Optional[str] = REQ( description: Optional[str] = REQ(
json_validator=check_capped_string(Stream.MAX_DESCRIPTION_LENGTH), default=None str_validator=check_capped_string(Stream.MAX_DESCRIPTION_LENGTH), default=None
), ),
is_private: Optional[bool] = REQ(json_validator=check_bool, default=None), is_private: Optional[bool] = REQ(json_validator=check_bool, default=None),
is_announcement_only: Optional[bool] = REQ(json_validator=check_bool, default=None), is_announcement_only: Optional[bool] = REQ(json_validator=check_bool, default=None),
@ -252,7 +252,7 @@ def update_stream_backend(
json_validator=check_int_in(Stream.STREAM_POST_POLICY_TYPES), default=None json_validator=check_int_in(Stream.STREAM_POST_POLICY_TYPES), default=None
), ),
history_public_to_subscribers: Optional[bool] = REQ(json_validator=check_bool, default=None), history_public_to_subscribers: Optional[bool] = REQ(json_validator=check_bool, default=None),
new_name: Optional[str] = REQ(json_validator=check_string, default=None), new_name: Optional[str] = REQ(default=None),
message_retention_days: Optional[Union[int, str]] = REQ( message_retention_days: Optional[Union[int, str]] = REQ(
json_validator=check_string_or_int, default=None json_validator=check_string_or_int, default=None
), ),