Repository: zulip/zulip

Issue:
`update_subscription_properties_backend`: Don't return the request in the response.
Tim Abbott: In our API endpoint for editing a stream's properties: https://chat.zulip.org/api/update-subscription-properties

We appear to have an unusual return value scheme, where we return basically what the client sent rather than an empty HTTP response.  While there's potentially some mild utility for communicating what changes were confirmed by the server, it feels to me like this is probably just a bug; none of our other POST/PATCH endpoints for doing a write have this pattern of returning what you just sent (which was a pattern used in the early days of Zulip, before we had the real-time events system).

The fix is to just have this endpoint `return json_success()` rather than gathering a response.  I believe no known clients actually look at the response from this endpoint, so we can just make the change (bumping `API_FEATURE_LEVEL` appropriately).

See https://chat.zulip.org/#narrow/stream/3-backend/topic/update-subscription-properties/near/900984 for discussion.

Tagging as a priority since this sort of API cleanup is valuable to do sooner rather than later.


Discussion:
Hello @zulip/server-api members, this issue was labeled with the "area: api" label, so you may want to check it out!

<!-- areaLabelAddition -->
@zulipbot claim
Hello @akashaviator, you have been unassigned from this issue because you have not updated this issue or any referenced pull requests for over 14 days.

You can reclaim this issue or claim any other issue by commenting `@zulipbot claim` on that issue.

Thanks for your contributions, and hope to see you again soon!
https://github.com/zulip/zulip/pull/20332#issuecomment-975864248 has a detailed technical discussion of the changes to be made here.  Once we finish this, we should audit for other endpoints with this structure and migrate them as well.

Resolving patch:
diff --git a/version.py b/version.py
--- a/version.py
+++ b/version.py
@@ -33,7 +33,7 @@
 # Changes should be accompanied by documentation explaining what the
 # new level means in templates/zerver/api/changelog.md, as well as
 # "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
-API_FEATURE_LEVEL = 110
+API_FEATURE_LEVEL = 111
 
 # 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
diff --git a/zerver/views/streams.py b/zerver/views/streams.py
--- a/zerver/views/streams.py
+++ b/zerver/views/streams.py
@@ -877,7 +877,6 @@ def update_subscription_properties_backend(
         "pin_to_top": check_bool,
         "wildcard_mentions_notify": check_bool,
     }
-    response_data = []
 
     for change in subscription_data:
         stream_id = change["stream_id"]
@@ -900,6 +899,16 @@ def update_subscription_properties_backend(
             user_profile, sub, stream, property, value, acting_user=user_profile
         )
 
-        response_data.append({"stream_id": stream_id, "property": property, "value": value})
+    # TODO: Do this more generally, see update_realm_user_settings_defaults.realm.py
+    from zerver.lib.request import RequestNotes
 
-    return json_success({"subscription_data": response_data})
+    request_notes = RequestNotes.get_notes(request)
+    for req_var in request.POST:
+        if req_var not in request_notes.processed_parameters:
+            request_notes.ignored_parameters.add(req_var)
+
+    result: Dict[str, Any] = {}
+    if len(request_notes.ignored_parameters) > 0:
+        result["ignored_parameters_unsupported"] = list(request_notes.ignored_parameters)
+
+    return json_success(result)


Test patch:
diff --git a/zerver/tests/test_subs.py b/zerver/tests/test_subs.py
--- a/zerver/tests/test_subs.py
+++ b/zerver/tests/test_subs.py
@@ -2729,6 +2729,40 @@ def test_set_invalid_property(self) -> None:
         )
         self.assert_json_error(result, "Unknown subscription property: bad")
 
+    def test_ignored_parameters_in_subscriptions_properties_endpoint(self) -> None:
+        """
+        Sending an invalid parameter with a valid parameter returns
+        an `ignored_parameters_unsupported` array.
+        """
+        test_user = self.example_user("hamlet")
+        self.login_user(test_user)
+
+        subs = gather_subscriptions(test_user)[0]
+        sub = subs[0]
+        json_result = self.api_post(
+            test_user,
+            "/api/v1/users/me/subscriptions/properties",
+            {
+                "subscription_data": orjson.dumps(
+                    [
+                        {
+                            "property": "wildcard_mentions_notify",
+                            "stream_id": sub["stream_id"],
+                            "value": True,
+                        }
+                    ]
+                ).decode(),
+                "invalid_parameter": orjson.dumps(
+                    [{"property": "pin_to_top", "stream_id": sub["stream_id"], "value": False}]
+                ).decode(),
+            },
+        )
+
+        self.assert_json_success(json_result)
+        result = orjson.loads(json_result.content)
+        self.assertIn("ignored_parameters_unsupported", result)
+        self.assertEqual(result["ignored_parameters_unsupported"], ["invalid_parameter"])
+
 
 class SubscriptionRestApiTest(ZulipTestCase):
     def test_basic_add_delete(self) -> None: