mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
channel-folders: Add PATCH method to reorder channel folders.
The test cases are copied from ReorderCustomProfileFieldTest since we are imitating the reordering mechanism from custom profile fields to channel folders.
This commit is contained in:
committed by
Tim Abbott
parent
40132e200b
commit
22b231ab6f
@@ -74,6 +74,7 @@
|
||||
* [Remove a default channel](/api/remove-default-stream)
|
||||
* [Create a channel folder](/api/create-channel-folder)
|
||||
* [Get channel folders](/api/get-channel-folders)
|
||||
* [Reorder channel folders](/api/patch-channel-folders)
|
||||
* [Update a channel folder](/api/update-channel-folder)
|
||||
|
||||
#### Users
|
||||
|
@@ -4,3 +4,6 @@
|
||||
Added a new field `order` to show in which order should channel folders be
|
||||
displayed. The list is 0-indexed and works similar to the `order` field of
|
||||
custom profile fields.
|
||||
* [`PATCH /channel_folders`](/api/patch-channel-folders): Added a new
|
||||
endpoint for reordering channel folders. It accepts an array of channel
|
||||
folder IDs arranged in the order the user desires it to be in.
|
||||
|
@@ -1,7 +1,11 @@
|
||||
from collections.abc import Iterable
|
||||
|
||||
from django.db import transaction
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from zerver.lib.channel_folders import get_channel_folder_dict, render_channel_folder_description
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.models import ChannelFolder, Realm, RealmAuditLog, UserProfile
|
||||
from zerver.models.realm_audit_logs import AuditLogEventType
|
||||
from zerver.models.users import active_user_ids
|
||||
@@ -22,6 +26,8 @@ def check_add_channel_folder(
|
||||
rendered_description=rendered_description,
|
||||
creator_id=acting_user.id,
|
||||
)
|
||||
channel_folder.order = channel_folder.id
|
||||
channel_folder.save(update_fields=["order"])
|
||||
|
||||
creation_time = timezone_now()
|
||||
RealmAuditLog.objects.create(
|
||||
@@ -42,6 +48,18 @@ def check_add_channel_folder(
|
||||
return channel_folder
|
||||
|
||||
|
||||
@transaction.atomic(durable=True)
|
||||
def try_reorder_realm_channel_folders(realm: Realm, order: Iterable[int]) -> None:
|
||||
order_mapping = {_[1]: _[0] for _ in enumerate(order)}
|
||||
channel_folders = ChannelFolder.objects.filter(realm=realm)
|
||||
for channel_folder in channel_folders:
|
||||
if channel_folder.id not in order_mapping:
|
||||
raise JsonableError(_("Invalid order mapping."))
|
||||
for channel_folder in channel_folders:
|
||||
channel_folder.order = order_mapping[channel_folder.id]
|
||||
channel_folder.save(update_fields=["order"])
|
||||
|
||||
|
||||
def do_send_channel_folder_update_event(
|
||||
channel_folder: ChannelFolder, data: dict[str, str | bool]
|
||||
) -> None:
|
||||
|
@@ -11,6 +11,7 @@ from typing import Any
|
||||
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
from zerver.actions.channel_folders import check_add_channel_folder
|
||||
from zerver.actions.create_user import do_create_user
|
||||
from zerver.actions.presence import update_user_presence
|
||||
from zerver.actions.reactions import do_add_reaction
|
||||
@@ -21,6 +22,7 @@ from zerver.lib.initial_password import initial_password
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.upload import upload_message_attachment
|
||||
from zerver.models import Client, Message, NamedUserGroup, UserPresence
|
||||
from zerver.models.channel_folders import ChannelFolder
|
||||
from zerver.models.realms import get_realm
|
||||
from zerver.models.users import UserProfile, get_user
|
||||
from zerver.openapi.openapi import Parameter
|
||||
@@ -389,3 +391,25 @@ def remove_attachment() -> dict[str, object]:
|
||||
attachment_id = url.replace("/user_uploads/", "").split("/")[0]
|
||||
|
||||
return {"attachment_id": attachment_id}
|
||||
|
||||
|
||||
@openapi_param_value_generator(["/channel_folders:patch"])
|
||||
def add_channel_folders() -> dict[str, object]:
|
||||
user_profile = helpers.example_user("iago")
|
||||
realm = user_profile.realm
|
||||
check_add_channel_folder(
|
||||
realm,
|
||||
"General",
|
||||
"Channel for general discussions",
|
||||
acting_user=user_profile,
|
||||
)
|
||||
check_add_channel_folder(
|
||||
realm,
|
||||
"Documentation",
|
||||
"Channels for **documentation** discussions",
|
||||
acting_user=user_profile,
|
||||
)
|
||||
check_add_channel_folder(realm, "Memes", "Channels for sharing memes", acting_user=user_profile)
|
||||
channel_folders = ChannelFolder.objects.filter(realm=realm)
|
||||
|
||||
return {"order": [folder.id for folder in channel_folders]}
|
||||
|
@@ -24516,6 +24516,52 @@ paths:
|
||||
},
|
||||
],
|
||||
}
|
||||
patch:
|
||||
operationId: patch-channel-folders
|
||||
summary: Reorder channel folders
|
||||
tags: ["channels"]
|
||||
description: |
|
||||
Given an array of channel folder IDs, this method will set the `order`
|
||||
property of all of the channel folders in the organization according to
|
||||
the order of the channel folder IDs specified in the request.
|
||||
|
||||
**Changes**: New in Zulip 11.0 (feature level ZF-fcae8c).
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
order:
|
||||
type: array
|
||||
description: |
|
||||
A list of channel folder IDs representing the new order.
|
||||
items:
|
||||
type: integer
|
||||
encoding:
|
||||
order:
|
||||
contentType: application/json
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/SimpleSuccess"
|
||||
|
||||
"400":
|
||||
description: Bad request.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/CodedError"
|
||||
- example:
|
||||
{
|
||||
"code": "BAD_REQUEST",
|
||||
"msg": "Invalid order mapping",
|
||||
"result": "error",
|
||||
}
|
||||
description: |
|
||||
An example JSON response when the order mapping is invalid:
|
||||
|
||||
/channel_folders/{channel_folder_id}:
|
||||
patch:
|
||||
operationId: update-channel-folder
|
||||
|
@@ -21,7 +21,10 @@ class ChannelFoldersTestCase(ZulipTestCase):
|
||||
lear_user = self.lear_user("cordelia")
|
||||
|
||||
check_add_channel_folder(
|
||||
zulip_realm, "Frontend", "Channels for frontend discussions", acting_user=iago
|
||||
zulip_realm,
|
||||
"Frontend",
|
||||
"Channels for frontend discussions",
|
||||
acting_user=iago,
|
||||
)
|
||||
check_add_channel_folder(
|
||||
zulip_realm, "Backend", "Channels for **backend** discussions", acting_user=iago
|
||||
@@ -50,6 +53,7 @@ class ChannelFolderCreationTest(ZulipTestCase):
|
||||
assert channel_folder is not None
|
||||
self.assertEqual(channel_folder.name, "Frontend")
|
||||
self.assertEqual(channel_folder.description, "")
|
||||
self.assertEqual(channel_folder.id, channel_folder.order)
|
||||
response = orjson.loads(result.content)
|
||||
self.assertEqual(response["channel_folder_id"], channel_folder.id)
|
||||
|
||||
@@ -349,3 +353,65 @@ class UpdateChannelFoldersTest(ChannelFoldersTestCase):
|
||||
self.assert_json_success(result)
|
||||
channel_folder = ChannelFolder.objects.get(id=channel_folder_id)
|
||||
self.assertTrue(channel_folder.is_archived)
|
||||
|
||||
|
||||
class ReorderChannelFolderTest(ChannelFoldersTestCase):
|
||||
def test_reorder(self) -> None:
|
||||
self.login("iago")
|
||||
realm = get_realm("zulip")
|
||||
order = list(
|
||||
ChannelFolder.objects.filter(realm=realm)
|
||||
.order_by("-order")
|
||||
.values_list("order", flat=True)
|
||||
)
|
||||
result = self.client_patch(
|
||||
"/json/channel_folders", info={"order": orjson.dumps(order).decode()}
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
fields = ChannelFolder.objects.filter(realm=realm).order_by("order")
|
||||
for field in fields:
|
||||
self.assertEqual(field.id, order[field.order])
|
||||
|
||||
def test_reorder_duplicates(self) -> None:
|
||||
self.login("iago")
|
||||
realm = get_realm("zulip")
|
||||
order = list(
|
||||
ChannelFolder.objects.filter(realm=realm)
|
||||
.order_by("-order")
|
||||
.values_list("order", flat=True)
|
||||
)
|
||||
frontend_folder = ChannelFolder.objects.get(name="Frontend", realm=realm)
|
||||
order.append(frontend_folder.id)
|
||||
result = self.client_patch(
|
||||
"/json/channel_folders", info={"order": orjson.dumps(order).decode()}
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
fields = ChannelFolder.objects.filter(realm=realm).order_by("order")
|
||||
for field in fields:
|
||||
self.assertEqual(field.id, order[field.order])
|
||||
|
||||
def test_reorder_unauthorized(self) -> None:
|
||||
self.login("hamlet")
|
||||
realm = get_realm("zulip")
|
||||
order = list(
|
||||
ChannelFolder.objects.filter(realm=realm)
|
||||
.order_by("-order")
|
||||
.values_list("order", flat=True)
|
||||
)
|
||||
result = self.client_patch(
|
||||
"/json/channel_folders", info={"order": orjson.dumps(order).decode()}
|
||||
)
|
||||
self.assert_json_error(result, "Must be an organization administrator")
|
||||
|
||||
def test_reorder_invalid(self) -> None:
|
||||
self.login("iago")
|
||||
order = [100, 200, 300]
|
||||
result = self.client_patch(
|
||||
"/json/channel_folders", info={"order": orjson.dumps(order).decode()}
|
||||
)
|
||||
self.assert_json_error(result, "Invalid order mapping.")
|
||||
order = [1, 2]
|
||||
result = self.client_patch(
|
||||
"/json/channel_folders", info={"order": orjson.dumps(order).decode()}
|
||||
)
|
||||
self.assert_json_error(result, "Invalid order mapping.")
|
||||
|
@@ -10,6 +10,7 @@ from zerver.actions.channel_folders import (
|
||||
do_change_channel_folder_description,
|
||||
do_change_channel_folder_name,
|
||||
do_unarchive_channel_folder,
|
||||
try_reorder_realm_channel_folders,
|
||||
)
|
||||
from zerver.decorator import require_realm_admin
|
||||
from zerver.lib.channel_folders import (
|
||||
@@ -52,6 +53,18 @@ def get_channel_folders(
|
||||
return json_success(request, data={"channel_folders": channel_folders})
|
||||
|
||||
|
||||
@require_realm_admin
|
||||
@typed_endpoint
|
||||
def reorder_realm_channel_folders(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
order: Json[list[int]],
|
||||
) -> HttpResponse:
|
||||
try_reorder_realm_channel_folders(user_profile.realm, order)
|
||||
return json_success(request)
|
||||
|
||||
|
||||
@require_realm_admin
|
||||
@typed_endpoint
|
||||
def update_channel_folder(
|
||||
|
@@ -48,6 +48,7 @@ from zerver.views.auth import (
|
||||
from zerver.views.channel_folders import (
|
||||
create_channel_folder,
|
||||
get_channel_folders,
|
||||
reorder_realm_channel_folders,
|
||||
update_channel_folder,
|
||||
)
|
||||
from zerver.views.compatibility import check_global_compatibility
|
||||
@@ -559,7 +560,7 @@ v1_api_and_json_patterns = [
|
||||
DELETE=remove_subscriptions_backend,
|
||||
),
|
||||
rest_path("channel_folders/create", POST=create_channel_folder),
|
||||
rest_path("channel_folders", GET=get_channel_folders),
|
||||
rest_path("channel_folders", GET=get_channel_folders, PATCH=reorder_realm_channel_folders),
|
||||
rest_path("channel_folders/<int:channel_folder_id>", PATCH=update_channel_folder),
|
||||
# topic-muting -> zerver.views.user_topics
|
||||
# (deprecated and will be removed once clients are migrated to use '/user_topics')
|
||||
|
Reference in New Issue
Block a user