data_import: Set a default announcements channel for 3rd party imports.

For Slack, Mattermost and RocketChat, set the default announcement
channels if the sane default channel is available in the import.

The next commit will add a mechanism to choose an alternative default if
the expected channel name is not present.

Addresses the major part of #34984.
This commit is contained in:
Mateusz Mandera
2025-07-19 02:59:24 +08:00
committed by Tim Abbott
parent 715d07c231
commit 342c00cc7b
6 changed files with 89 additions and 11 deletions

View File

@@ -140,7 +140,11 @@ def convert_user_data(
user_handler.validate_user_emails()
MATTERMOST_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME = "Town Square"
def convert_channel_data(
realm: ZerverFieldsT,
channel_data: list[ZerverFieldsT],
user_data_map: dict[str, dict[str, Any]],
subscriber_handler: SubscriberHandler,
@@ -149,6 +153,8 @@ def convert_channel_data(
realm_id: int,
team_name: str,
) -> list[ZerverFieldsT]:
zerver_realm = realm["zerver_realm"]
channel_data_list = [d for d in channel_data if d["team"] == team_name]
channel_members_map: dict[str, list[str]] = {}
@@ -223,6 +229,11 @@ def convert_channel_data(
users=channel_users,
stream_id=stream_id,
)
if channel_dict["display_name"] == MATTERMOST_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME:
zerver_realm[0]["new_stream_announcements_stream"] = stream["id"]
zerver_realm[0]["zulip_update_announcements_stream"] = stream["id"]
streams.append(stream)
return streams
@@ -923,6 +934,7 @@ def do_convert_data(mattermost_data_dir: str, output_dir: str, masking_content:
)
zerver_stream = convert_channel_data(
realm=realm,
channel_data=mattermost_data["channel"],
user_data_map=username_to_user,
subscriber_handler=subscriber_handler,

View File

@@ -157,12 +157,18 @@ def get_stream_name(rc_channel: dict[str, Any]) -> str:
return stream_name
ROCKETCHAT_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME = "general"
def convert_channel_data(
realm: ZerverFieldsT,
room_id_to_room_map: dict[str, dict[str, Any]],
team_id_to_team_map: dict[str, dict[str, Any]],
stream_id_mapper: IdMapper[str],
realm_id: int,
) -> list[ZerverFieldsT]:
zerver_realm = realm["zerver_realm"]
streams = []
for rc_room_id, channel_dict in room_id_to_room_map.items():
@@ -196,6 +202,11 @@ def convert_channel_data(
invite_only=invite_only,
stream_post_policy=stream_post_policy,
)
if stream_name == ROCKETCHAT_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME:
zerver_realm[0]["new_stream_announcements_stream"] = stream["id"]
zerver_realm[0]["zulip_update_announcements_stream"] = stream["id"]
streams.append(stream)
return streams
@@ -1133,6 +1144,7 @@ def do_convert_data(rocketchat_data_dir: str, output_dir: str) -> None:
)
zerver_stream = convert_channel_data(
realm=realm,
room_id_to_room_map=room_id_to_room_map,
team_id_to_team_map=team_id_to_team_map,
stream_id_mapper=stream_id_mapper,

View File

@@ -537,6 +537,9 @@ def get_user_timezone(user: ZerverFieldsT) -> str:
return timezone
SLACK_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME = "general"
def channels_to_zerver_stream(
slack_data_dir: str,
realm_id: int,
@@ -558,6 +561,8 @@ def channels_to_zerver_stream(
"""
logging.info("######### IMPORTING CHANNELS STARTED #########\n")
zerver_realm = realm["zerver_realm"]
added_channels = {}
added_mpims = {}
dm_members = {}
@@ -619,6 +624,13 @@ def channels_to_zerver_stream(
recipient_id_count += 1
logging.info("%s -> created", channel["name"])
if channel["name"] == SLACK_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME:
zerver_realm[0]["new_stream_announcements_stream"] = stream["id"]
zerver_realm[0]["zulip_update_announcements_stream"] = stream["id"]
logging.info(
"Using the channel %s as default announcements channel.", channel["name"]
)
# TODO map Slack's pins to Zulip's stars
# There is the security model that Slack's pins are known to the team owner
# as evident from where it is stored at (channels)

View File

@@ -224,6 +224,9 @@ class MatterMostImporter(ZulipTestCase):
user_id_mapper = IdMapper[str]()
team_name = "gryffindor"
mock_realm_dict: ZerverFieldsT = dict(zerver_realm=[dict()])
zerver_realm = mock_realm_dict["zerver_realm"]
convert_user_data(
user_handler=user_handler,
user_id_mapper=user_id_mapper,
@@ -232,15 +235,20 @@ class MatterMostImporter(ZulipTestCase):
team_name=team_name,
)
zerver_stream = convert_channel_data(
channel_data=mattermost_data["channel"],
user_data_map=username_to_user,
subscriber_handler=subscriber_handler,
stream_id_mapper=stream_id_mapper,
user_id_mapper=user_id_mapper,
realm_id=3,
team_name=team_name,
)
with patch(
"zerver.data_import.mattermost.MATTERMOST_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME",
"Gryffindor common room",
):
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
channel_data=mattermost_data["channel"],
user_data_map=username_to_user,
subscriber_handler=subscriber_handler,
stream_id_mapper=stream_id_mapper,
user_id_mapper=user_id_mapper,
realm_id=3,
team_name=team_name,
)
self.assert_length(zerver_stream, 3)
@@ -252,6 +260,11 @@ class MatterMostImporter(ZulipTestCase):
self.assertEqual(zerver_stream[0]["rendered_description"], "")
self.assertEqual(zerver_stream[0]["realm"], 3)
self.assertEqual(
zerver_realm[0]["zulip_update_announcements_stream"], zerver_stream[0]["id"]
)
self.assertEqual(zerver_realm[0]["new_stream_announcements_stream"], zerver_stream[0]["id"])
self.assertEqual(zerver_stream[1]["name"], "Gryffindor quidditch team")
self.assertEqual(zerver_stream[1]["invite_only"], False)
self.assertEqual(
@@ -293,7 +306,9 @@ class MatterMostImporter(ZulipTestCase):
# Converting channel data when a user's `teams` value is `null`.
username_to_user["ron"].update(teams=None)
mock_realm_dict = dict(zerver_realm=[dict()])
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
channel_data=mattermost_data["channel"],
user_data_map=username_to_user,
subscriber_handler=subscriber_handler,
@@ -320,7 +335,9 @@ class MatterMostImporter(ZulipTestCase):
)
team_name = "slytherin"
mock_realm_dict = dict(zerver_realm=[dict()])
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
channel_data=mattermost_data["channel"],
user_data_map=username_to_user,
subscriber_handler=subscriber_handler,

View File

@@ -258,6 +258,8 @@ class RocketChatImporter(ZulipTestCase):
fixture_dir_name = self.fixture_file_name("", "rocketchat_fixtures")
rocketchat_data = rocketchat_data_to_dict(fixture_dir_name)
mock_realm_dict: ZerverFieldsT = dict(zerver_realm=[dict()])
zerver_realm = mock_realm_dict["zerver_realm"]
realm_id = 3
stream_id_mapper = IdMapper[str]()
@@ -280,6 +282,7 @@ class RocketChatImporter(ZulipTestCase):
)
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
room_id_to_room_map=room_id_to_room_map,
team_id_to_team_map=team_id_to_team_map,
stream_id_mapper=stream_id_mapper,
@@ -298,6 +301,11 @@ class RocketChatImporter(ZulipTestCase):
self.assertEqual(zerver_stream[0]["stream_post_policy"], 1)
self.assertEqual(zerver_stream[0]["realm"], realm_id)
self.assertEqual(
zerver_realm[0]["zulip_update_announcements_stream"], zerver_stream[0]["id"]
)
self.assertEqual(zerver_realm[0]["new_stream_announcements_stream"], zerver_stream[0]["id"])
# Private stream
self.assertEqual(zerver_stream[1]["name"], "random")
self.assertEqual(zerver_stream[1]["invite_only"], True)
@@ -328,6 +336,7 @@ class RocketChatImporter(ZulipTestCase):
fixture_dir_name = self.fixture_file_name("", "rocketchat_fixtures")
rocketchat_data = rocketchat_data_to_dict(fixture_dir_name)
mock_realm_dict: ZerverFieldsT = dict(zerver_realm=[dict()])
realm_id = 3
domain_name = "zulip.com"
@@ -365,6 +374,7 @@ class RocketChatImporter(ZulipTestCase):
)
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
room_id_to_room_map=room_id_to_room_map,
team_id_to_team_map=team_id_to_team_map,
stream_id_mapper=stream_id_mapper,
@@ -413,6 +423,7 @@ class RocketChatImporter(ZulipTestCase):
room_id_to_room_map[no_user_channel["_id"]] = no_user_channel
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
room_id_to_room_map=room_id_to_room_map,
team_id_to_team_map=team_id_to_team_map,
stream_id_mapper=stream_id_mapper,
@@ -537,6 +548,7 @@ class RocketChatImporter(ZulipTestCase):
fixture_dir_name = self.fixture_file_name("", "rocketchat_fixtures")
rocketchat_data = rocketchat_data_to_dict(fixture_dir_name)
mock_realm_dict: ZerverFieldsT = dict(zerver_realm=[dict()])
realm_id = 3
domain_name = "zulip.com"
@@ -575,6 +587,7 @@ class RocketChatImporter(ZulipTestCase):
)
zerver_stream = convert_channel_data(
realm=mock_realm_dict,
room_id_to_room_map=room_id_to_room_map,
team_id_to_team_map=team_id_to_team_map,
stream_id_mapper=stream_id_mapper,

View File

@@ -927,8 +927,15 @@ class SlackImporter(ZulipTestCase):
}
zerver_userprofile = [{"id": 1}, {"id": 8}, {"id": 7}, {"id": 5}]
realm_id = 3
realm: ZerverFieldsT = {"zerver_userpresence": [], "zerver_realm": [dict()]}
zerver_realm = realm["zerver_realm"]
with self.assertLogs(level="INFO"):
with (
self.assertLogs(level="INFO"),
mock.patch(
"zerver.data_import.slack.SLACK_DEFAULT_ANNOUNCEMENTS_CHANNEL_NAME", "random"
),
):
(
realm,
added_channels,
@@ -938,7 +945,7 @@ class SlackImporter(ZulipTestCase):
) = channels_to_zerver_stream(
self.fixture_file_name("", "slack_fixtures"),
realm_id,
{"zerver_userpresence": []},
realm,
slack_user_id_to_zulip_user_id,
zerver_userprofile,
)
@@ -1007,6 +1014,11 @@ class SlackImporter(ZulipTestCase):
self.assertEqual(zerver_stream[0]["realm"], realm_id)
self.assertEqual(zerver_stream[2]["id"], test_added_channels[zerver_stream[2]["name"]][1])
self.assertEqual(
zerver_realm[0]["zulip_update_announcements_stream"], zerver_stream[0]["id"]
)
self.assertEqual(zerver_realm[0]["new_stream_announcements_stream"], zerver_stream[0]["id"])
self.assertEqual(self.get_set(realm["zerver_huddle"], "id"), {0, 1, 2})
self.assertEqual(realm["zerver_userpresence"], [])