diff --git a/changelog.d/10214.feature b/changelog.d/10214.feature
new file mode 100644
index 0000000000..a3818c9d25
--- /dev/null
+++ b/changelog.d/10214.feature
@@ -0,0 +1 @@
+Omit empty fields from the `/sync` response. Contributed by @deepbluev7.
\ No newline at end of file
diff --git a/synapse/rest/client/v2_alpha/sync.py b/synapse/rest/client/v2_alpha/sync.py
index 042e1788b6..ecbbcf3851 100644
--- a/synapse/rest/client/v2_alpha/sync.py
+++ b/synapse/rest/client/v2_alpha/sync.py
@@ -13,6 +13,7 @@
# limitations under the License.
import itertools
import logging
+from collections import defaultdict
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Tuple
from synapse.api.constants import Membership, PresenceState
@@ -232,29 +233,51 @@ class SyncRestServlet(RestServlet):
)
logger.debug("building sync response dict")
- return {
- "account_data": {"events": sync_result.account_data},
- "to_device": {"events": sync_result.to_device},
- "device_lists": {
- "changed": list(sync_result.device_lists.changed),
- "left": list(sync_result.device_lists.left),
- },
- "presence": SyncRestServlet.encode_presence(sync_result.presence, time_now),
- "rooms": {
- Membership.JOIN: joined,
- Membership.INVITE: invited,
- Membership.KNOCK: knocked,
- Membership.LEAVE: archived,
- },
- "groups": {
- Membership.JOIN: sync_result.groups.join,
- Membership.INVITE: sync_result.groups.invite,
- Membership.LEAVE: sync_result.groups.leave,
- },
- "device_one_time_keys_count": sync_result.device_one_time_keys_count,
- "org.matrix.msc2732.device_unused_fallback_key_types": sync_result.device_unused_fallback_key_types,
- "next_batch": await sync_result.next_batch.to_string(self.store),
- }
+
+ response: dict = defaultdict(dict)
+ response["next_batch"] = await sync_result.next_batch.to_string(self.store)
+
+ if sync_result.account_data:
+ response["account_data"] = {"events": sync_result.account_data}
+ if sync_result.presence:
+ response["presence"] = SyncRestServlet.encode_presence(
+ sync_result.presence, time_now
+ )
+
+ if sync_result.to_device:
+ response["to_device"] = {"events": sync_result.to_device}
+
+ if sync_result.device_lists.changed:
+ response["device_lists"]["changed"] = list(sync_result.device_lists.changed)
+ if sync_result.device_lists.left:
+ response["device_lists"]["left"] = list(sync_result.device_lists.left)
+
+ if sync_result.device_one_time_keys_count:
+ response[
+ "device_one_time_keys_count"
+ ] = sync_result.device_one_time_keys_count
+ if sync_result.device_unused_fallback_key_types:
+ response[
+ "org.matrix.msc2732.device_unused_fallback_key_types"
+ ] = sync_result.device_unused_fallback_key_types
+
+ if joined:
+ response["rooms"][Membership.JOIN] = joined
+ if invited:
+ response["rooms"][Membership.INVITE] = invited
+ if knocked:
+ response["rooms"][Membership.KNOCK] = knocked
+ if archived:
+ response["rooms"][Membership.LEAVE] = archived
+
+ if sync_result.groups.join:
+ response["groups"][Membership.JOIN] = sync_result.groups.join
+ if sync_result.groups.invite:
+ response["groups"][Membership.INVITE] = sync_result.groups.invite
+ if sync_result.groups.leave:
+ response["groups"][Membership.LEAVE] = sync_result.groups.leave
+
+ return response
@staticmethod
def encode_presence(events, time_now):
diff --git a/tests/rest/client/v2_alpha/test_sync.py b/tests/rest/client/v2_alpha/test_sync.py
index 012910f136..cdca3a3e23 100644
--- a/tests/rest/client/v2_alpha/test_sync.py
+++ b/tests/rest/client/v2_alpha/test_sync.py
@@ -41,35 +41,7 @@ class FilterTestCase(unittest.HomeserverTestCase):
channel = self.make_request("GET", "/sync")
self.assertEqual(channel.code, 200)
- self.assertTrue(
- {
- "next_batch",
- "rooms",
- "presence",
- "account_data",
- "to_device",
- "device_lists",
- }.issubset(set(channel.json_body.keys()))
- )
-
- def test_sync_presence_disabled(self):
- """
- When presence is disabled, the key does not appear in /sync.
- """
- self.hs.config.use_presence = False
-
- channel = self.make_request("GET", "/sync")
-
- self.assertEqual(channel.code, 200)
- self.assertTrue(
- {
- "next_batch",
- "rooms",
- "account_data",
- "to_device",
- "device_lists",
- }.issubset(set(channel.json_body.keys()))
- )
+ self.assertIn("next_batch", channel.json_body)
class SyncFilterTestCase(unittest.HomeserverTestCase):
diff --git a/tests/server_notices/test_resource_limits_server_notices.py b/tests/server_notices/test_resource_limits_server_notices.py
index d46521ccdc..3245aa91ca 100644
--- a/tests/server_notices/test_resource_limits_server_notices.py
+++ b/tests/server_notices/test_resource_limits_server_notices.py
@@ -306,8 +306,9 @@ class TestResourceLimitsServerNoticesWithRealRooms(unittest.HomeserverTestCase):
channel = self.make_request("GET", "/sync?timeout=0", access_token=tok)
- invites = channel.json_body["rooms"]["invite"]
- self.assertEqual(len(invites), 0, invites)
+ self.assertNotIn(
+ "rooms", channel.json_body, "Got invites without server notice"
+ )
def test_invite_with_notice(self):
"""Tests that, if the MAU limit is hit, the server notices user invites each user
@@ -364,7 +365,8 @@ class TestResourceLimitsServerNoticesWithRealRooms(unittest.HomeserverTestCase):
# We could also pick another user and sync with it, which would return an
# invite to a system notices room, but it doesn't matter which user we're
# using so we use the last one because it saves us an extra sync.
- invites = channel.json_body["rooms"]["invite"]
+ if "rooms" in channel.json_body:
+ invites = channel.json_body["rooms"]["invite"]
# Make sure we have an invite to process.
self.assertEqual(len(invites), 1, invites)
|