Browse Source

Track Chatto 0.5.0a5

Bring chattolib up to Chatto v0.5.0-alpha.5. The wire delta is small:
two new RPCs on AdminRoomLayoutService (MoveRoomGroup, MoveSidebarItem)
and a set of reserved-field removals (Neighbor.testimonial,
ListNeighborsResponse.neighbors) that carry no client-side change.

- Regenerated proto bindings + Connect stubs from v0.5.0-alpha.5.
- Added ChattoClient.admin_move_room_group and admin_move_sidebar_item
  wrappers, mirroring the existing room-layout methods.
- Updated the AGENTS.md service table.

Bumped version 0.5.0a4.post1 -> 0.5.0a5.
Federico Feliziani 2 tháng trước cách đây
mục cha
commit
bb71ccb3ce

+ 1 - 1
AGENTS.md

@@ -104,7 +104,7 @@ to its grants.
 | Assets | `AssetService` | `GetAsset`, `BatchGetAssets` |
 | Asset uploads | `AssetUploadService` | `CreateUpload`, `UploadChunk`, `GetUpload`, `CompleteUpload`, `CancelUpload`. `upload_attachment(room, path)` helper computes SHA-256, chunks, and completes in one call. |
 | Admin: server | `chatto.admin.v1.AdminServerService` | `GetServerConfig`, `UpdateServerConfig`, `UploadServerLogo`, `DeleteServerLogo`, `UploadServerBanner`, `DeleteServerBanner`, `GetServerSecurityConfig`, `UpdateBlockedUsernames` |
-| Admin: room layout | `chatto.admin.v1.AdminRoomLayoutService` | `ListRoomGroups`, `Create/Update/Delete/ReorderRoomGroup(s)`, `MoveRoomToGroup`, `ReorderSidebarItemsInGroup`, `Create/Update/Delete/MoveSidebarLink(ToGroup)` |
+| Admin: room layout | `chatto.admin.v1.AdminRoomLayoutService` | `ListRoomGroups`, `Create/Update/Delete/ReorderRoomGroup(s)`, `MoveRoomGroup`, `MoveRoomToGroup`, `ReorderSidebarItemsInGroup`, `MoveSidebarItem`, `Create/Update/Delete/MoveSidebarLink(ToGroup)` |
 | Admin: users | `chatto.admin.v1.AdminUserService` | `ListMembers`, `GetMember`, `BatchGetMembers`, `AssignRole`, `RevokeRole`, `UpdateUser`, `UpdateUserPassword`, `ClearUsernameCooldown`, `DeleteUser` |
 | Admin: roles | `chatto.admin.v1.AdminRoleService` | `ListRoles`, `GetRole`, `CreateRole`, `UpdateRole`, `DeleteRole`, `ReorderRoles` |
 | Admin: event log | `chatto.admin.v1.AdminEventLogService` | `ListEvents`, `ListEventTypes`, `GetEvent` (raw response) |

+ 39 - 0
proto/chatto/admin/v1/room_layout.proto

@@ -165,6 +165,21 @@ message ReorderRoomGroupsResponse {
   repeated AdminRoomLayoutGroup groups = 1;
 }
 
+// Request to place one room group immediately before another group. When
+// before_group_id is absent, the group moves to the end of the sidebar.
+message MoveRoomGroupRequest {
+  // Required room group to move.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Group that follows the moved group. Omit to place the group last.
+  optional string before_group_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of moving one room group.
+message MoveRoomGroupResponse {
+  // Room groups in their authoritative order after the move.
+  repeated AdminRoomLayoutGroup groups = 1;
+}
+
 // Request to move a channel room to another room group.
 message MoveRoomToGroupRequest {
   // Required channel room ID.
@@ -193,6 +208,24 @@ message ReorderSidebarItemsInGroupResponse {
   AdminRoomLayoutGroup group = 1;
 }
 
+// Request to move one room or sidebar link to an exact sidebar position.
+// The server resolves the complete current layout, including entries that the
+// caller's ordinary room directory does not show.
+message MoveSidebarItemRequest {
+  // Required room or sidebar link to move.
+  AdminRoomLayoutItemInput item = 1 [(buf.validate.field).required = true];
+  // Required destination room group.
+  string group_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Item that follows the moved item. Omit to place the item last.
+  AdminRoomLayoutItemInput before = 3;
+}
+
+// Result of moving one sidebar item.
+message MoveSidebarItemResponse {
+  // Destination room group after the move.
+  AdminRoomLayoutGroup group = 1;
+}
+
 // Request to create a sidebar link in one room group.
 message CreateSidebarLinkRequest {
   // Required destination room group ID.
@@ -282,12 +315,18 @@ service AdminRoomLayoutService {
   rpc DeleteRoomGroup(DeleteRoomGroupRequest) returns (DeleteRoomGroupResponse);
   // Replaces the global room group order. Requires server-scope room.manage.
   rpc ReorderRoomGroups(ReorderRoomGroupsRequest) returns (ReorderRoomGroupsResponse);
+  // Moves one room group before another group, or to the end. Requires
+  // server-scope room.manage.
+  rpc MoveRoomGroup(MoveRoomGroupRequest) returns (MoveRoomGroupResponse);
   // Moves a channel room to another room group. Requires room.manage in both
   // source and destination groups.
   rpc MoveRoomToGroup(MoveRoomToGroupRequest) returns (MoveRoomToGroupResponse);
   // Replaces the mixed room/link sidebar order in one group. Requires
   // room.manage in that group.
   rpc ReorderSidebarItemsInGroup(ReorderSidebarItemsInGroupRequest) returns (ReorderSidebarItemsInGroupResponse);
+  // Moves one room or sidebar link to a position in a room group. Requires
+  // room.manage in the source and destination groups.
+  rpc MoveSidebarItem(MoveSidebarItemRequest) returns (MoveSidebarItemResponse);
   // Creates a sidebar link. Requires room.manage in the destination group.
   rpc CreateSidebarLink(CreateSidebarLinkRequest) returns (CreateSidebarLinkResponse);
   // Updates a sidebar link. Requires room.manage in the link's current group.

+ 13 - 3
proto/chatto/admin/v1/server.proto

@@ -142,6 +142,9 @@ message UpdateBlockedUsernamesResponse {
 
 // One server advertised in the public Neighbor directory.
 message Neighbor {
+  reserved 4;
+  reserved "testimonial";
+
   // Stable opaque resource ID.
   string id = 1;
   // Canonical HTTP or HTTPS server origin.
@@ -170,6 +173,9 @@ message GetNeighborResponse {
 
 // Request to advertise one server origin.
 message CreateNeighborRequest {
+  reserved 2;
+  reserved "testimonial";
+
   string origin = 1 [(buf.validate.field).string = {min_len: 1, max_len: 2048}];
 }
 
@@ -180,6 +186,9 @@ message CreateNeighborResponse {
 
 // Request to change one advertised server origin.
 message UpdateNeighborRequest {
+  reserved 4;
+  reserved "testimonial";
+
   string neighbor_id = 1 [(buf.validate.field).string = {min_len: 1, max_len: 64}];
   string origin = 2 [(buf.validate.field).string = {min_len: 1, max_len: 2048}];
   string revision = 3 [(buf.validate.field).string = {min_len: 1, max_len: 64}];
@@ -225,10 +234,11 @@ service AdminServerService {
   rpc ListNeighbors(ListNeighborsRequest) returns (ListNeighborsResponse);
   // Gets one configured Neighbor. This RPC requires server.manage-neighbors.
   rpc GetNeighbor(GetNeighborRequest) returns (GetNeighborResponse);
-  // Advertises one server origin. This RPC requires server.manage-neighbors.
+  // Advertises one server origin. The origin must not identify this server.
+  // This RPC requires server.manage-neighbors.
   rpc CreateNeighbor(CreateNeighborRequest) returns (CreateNeighborResponse);
-  // Changes one advertised server origin. This RPC requires
-  // server.manage-neighbors.
+  // Changes one advertised server origin. The new origin must not identify
+  // this server. This RPC requires server.manage-neighbors.
   rpc UpdateNeighbor(UpdateNeighborRequest) returns (UpdateNeighborResponse);
   // Stops advertising one server origin. This RPC requires
   // server.manage-neighbors.

+ 5 - 2
proto/chatto/discovery/v1/server.proto

@@ -20,9 +20,12 @@ message GetServerResponse {
 // Request for the public Neighbor directory.
 message ListNeighborsRequest {}
 
-// Publicly advertised canonical server origins. The response has no ordering
-// contract.
+// Publicly advertised servers. The response has no ordering contract.
 message ListNeighborsResponse {
+  reserved 2;
+  reserved "neighbors";
+
+  // Canonical origins of the advertised servers.
   repeated string origins = 1;
 }
 

+ 1 - 1
pyproject.toml

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "chattolib"
-version = "0.5.0a4.post1"
+version = "0.5.0a5"
 description = "Async Python client library for the Chatto webchat Connect API"
 requires-python = ">=3.11"
 license = "MPL-2.0 AND Apache-2.0"

+ 48 - 0
src/chattolib/_pb/chatto/admin/v1/room_layout_connect.py

@@ -90,6 +90,18 @@ class AdminRoomLayoutServiceClient(ConnectClient):
             headers=headers,
         )
 
+    async def move_room_group(self, request: _pb2.MoveRoomGroupRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=_pb2.MoveRoomGroupRequest,
+                output=_pb2.MoveRoomGroupResponse,
+            ),
+            headers=headers,
+        )
+
     async def move_room_to_group(self, request: _pb2.MoveRoomToGroupRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveRoomToGroupResponse:
         return await self.execute_unary(
             request=request,
@@ -114,6 +126,18 @@ class AdminRoomLayoutServiceClient(ConnectClient):
             headers=headers,
         )
 
+    async def move_sidebar_item(self, request: _pb2.MoveSidebarItemRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveSidebarItemResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveSidebarItem",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=_pb2.MoveSidebarItemRequest,
+                output=_pb2.MoveSidebarItemResponse,
+            ),
+            headers=headers,
+        )
+
     async def create_sidebar_link(self, request: _pb2.CreateSidebarLinkRequest, *, headers: dict[str, str] | None = None) -> _pb2.CreateSidebarLinkResponse:
         return await self.execute_unary(
             request=request,
@@ -248,6 +272,18 @@ class AdminRoomLayoutServiceClientSync(ConnectClientSync):
             headers=headers,
         )
 
+    def move_room_group(self, request: _pb2.MoveRoomGroupRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=_pb2.MoveRoomGroupRequest,
+                output=_pb2.MoveRoomGroupResponse,
+            ),
+            headers=headers,
+        )
+
     def move_room_to_group(self, request: _pb2.MoveRoomToGroupRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveRoomToGroupResponse:
         return self.execute_unary(
             request=request,
@@ -272,6 +308,18 @@ class AdminRoomLayoutServiceClientSync(ConnectClientSync):
             headers=headers,
         )
 
+    def move_sidebar_item(self, request: _pb2.MoveSidebarItemRequest, *, headers: dict[str, str] | None = None) -> _pb2.MoveSidebarItemResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveSidebarItem",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=_pb2.MoveSidebarItemRequest,
+                output=_pb2.MoveSidebarItemResponse,
+            ),
+            headers=headers,
+        )
+
     def create_sidebar_link(self, request: _pb2.CreateSidebarLinkRequest, *, headers: dict[str, str] | None = None) -> _pb2.CreateSidebarLinkResponse:
         return self.execute_unary(
             request=request,

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
src/chattolib/_pb/chatto/admin/v1/room_layout_pb2.py


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
src/chattolib/_pb/chatto/admin/v1/server_pb2.py


+ 4 - 4
src/chattolib/_pb/chatto/discovery/v1/server_pb2.py

@@ -26,7 +26,7 @@ from chatto.api.v1 import server_pb2 as chatto_dot_api_dot_v1_dot_server__pb2
 from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n chatto/discovery/v1/server.proto\x12\x13\x63hatto.discovery.v1\x1a\x1a\x63hatto/api/v1/server.proto\x1a google/protobuf/descriptor.proto\"\x12\n\x10GetServerRequest\"s\n\x11GetServerResponse\x12\x33\n\x07profile\x18\x01 \x01(\x0b\x32\".chatto.api.v1.ServerPublicProfile\x12)\n\x05login\x18\x02 \x01(\x0b\x32\x1a.chatto.api.v1.ServerLogin\"\x16\n\x14ListNeighborsRequest\"(\n\x15ListNeighborsResponse\x12\x0f\n\x07origins\x18\x01 \x03(\t2\xe6\x01\n\x16ServerDiscoveryService\x12_\n\tGetServer\x12%.chatto.discovery.v1.GetServerRequest\x1a&.chatto.discovery.v1.GetServerResponse\"\x03\x90\x02\x01\x12k\n\rListNeighbors\x12).chatto.discovery.v1.ListNeighborsRequest\x1a*.chatto.discovery.v1.ListNeighborsResponse\"\x03\x90\x02\x01\x62\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n chatto/discovery/v1/server.proto\x12\x13\x63hatto.discovery.v1\x1a\x1a\x63hatto/api/v1/server.proto\x1a google/protobuf/descriptor.proto\"\x12\n\x10GetServerRequest\"s\n\x11GetServerResponse\x12\x33\n\x07profile\x18\x01 \x01(\x0b\x32\".chatto.api.v1.ServerPublicProfile\x12)\n\x05login\x18\x02 \x01(\x0b\x32\x1a.chatto.api.v1.ServerLogin\"\x16\n\x14ListNeighborsRequest\"9\n\x15ListNeighborsResponse\x12\x0f\n\x07origins\x18\x01 \x03(\tJ\x04\x08\x02\x10\x03R\tneighbors2\xe6\x01\n\x16ServerDiscoveryService\x12_\n\tGetServer\x12%.chatto.discovery.v1.GetServerRequest\x1a&.chatto.discovery.v1.GetServerResponse\"\x03\x90\x02\x01\x12k\n\rListNeighbors\x12).chatto.discovery.v1.ListNeighborsRequest\x1a*.chatto.discovery.v1.ListNeighborsResponse\"\x03\x90\x02\x01\x62\x06proto3')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -44,7 +44,7 @@ if not _descriptor._USE_C_DESCRIPTORS:
   _globals['_LISTNEIGHBORSREQUEST']._serialized_start=256
   _globals['_LISTNEIGHBORSREQUEST']._serialized_end=278
   _globals['_LISTNEIGHBORSRESPONSE']._serialized_start=280
-  _globals['_LISTNEIGHBORSRESPONSE']._serialized_end=320
-  _globals['_SERVERDISCOVERYSERVICE']._serialized_start=323
-  _globals['_SERVERDISCOVERYSERVICE']._serialized_end=553
+  _globals['_LISTNEIGHBORSRESPONSE']._serialized_end=337
+  _globals['_SERVERDISCOVERYSERVICE']._serialized_start=340
+  _globals['_SERVERDISCOVERYSERVICE']._serialized_end=570
 # @@protoc_insertion_point(module_scope)

+ 42 - 0
src/chattolib/client.py

@@ -1734,6 +1734,23 @@ class ChattoClient:
             if g is not None
         ]
 
+    async def admin_move_room_group(
+        self, group_id: str, before_group_id: str | None = None
+    ) -> list[AdminRoomLayoutGroup]:
+        """Move one room group before another (or to the end if omitted)."""
+        req = room_layout_pb2.MoveRoomGroupRequest(group_id=group_id)
+        if before_group_id is not None:
+            req.before_group_id = before_group_id
+        resp = await self._rpc(
+            self._svc.admin_room_layout.move_room_group(req, headers=self._headers())
+        )
+        data = pb_to_dict(resp)
+        return [
+            g
+            for g in (AdminRoomLayoutGroup.parse(row) for row in data.get("groups") or [])
+            if g is not None
+        ]
+
     async def admin_move_room_to_group(self, room_id: str, group_id: str) -> Room:
         resp = await self._rpc(
             self._svc.admin_room_layout.move_room_to_group(
@@ -1762,6 +1779,31 @@ class ChattoClient:
         assert group is not None
         return group
 
+    async def admin_move_sidebar_item(
+        self,
+        item: tuple[AdminRoomLayoutItemKind, str],
+        group_id: str,
+        before: tuple[AdminRoomLayoutItemKind, str] | None = None,
+    ) -> AdminRoomLayoutGroup:
+        """Move one room or sidebar link to a position in a room group.
+
+        ``item`` is the ``(kind, id)`` of the entry to move; ``before`` is the
+        ``(kind, id)`` of the entry it should be placed ahead of (omit to place
+        it last in ``group_id``).
+        """
+        req = room_layout_pb2.MoveSidebarItemRequest(group_id=group_id)
+        req.item.kind = item[0].value
+        req.item.id = item[1]
+        if before is not None:
+            req.before.kind = before[0].value
+            req.before.id = before[1]
+        resp = await self._rpc(
+            self._svc.admin_room_layout.move_sidebar_item(req, headers=self._headers())
+        )
+        group = AdminRoomLayoutGroup.parse(pb_to_dict(resp.group))
+        assert group is not None
+        return group
+
     async def admin_create_sidebar_link(
         self, group_id: str, label: str, url: str
     ) -> dict[str, Any]:

+ 52 - 0
tests/test_client.py

@@ -41,6 +41,7 @@ from chattolib._pb.chatto.api.v1 import (
 from chattolib._pb.chatto.discovery.v1 import server_pb2 as discovery_server_pb2
 from chattolib.client import ChattoClient
 from chattolib.types import (
+    AdminRoomLayoutItemKind,
     ImageFitMode,
     ImageTransformOptions,
     NotificationLevel,
@@ -692,6 +693,57 @@ async def test_admin_create_sidebar_link(client):
     assert result == {"id": "sl1", "label": "Docs", "url": "https://x"}
 
 
+async def test_admin_move_room_group(client):
+    resp = room_layout_pb2.MoveRoomGroupResponse()
+    g = resp.groups.add()
+    g.id = "g1"
+    g.name = "General"
+    mock = _mock_method(client, "admin_room_layout", "move_room_group", resp)
+
+    async with client:
+        groups = await client.admin_move_room_group("g1", before_group_id="g2")
+
+    assert [g.id for g in groups] == ["g1"]
+    sent = mock.call_args.args[0]
+    assert sent.group_id == "g1"
+    assert sent.before_group_id == "g2"
+
+
+async def test_admin_move_room_group_to_end(client):
+    resp = room_layout_pb2.MoveRoomGroupResponse()
+    mock = _mock_method(client, "admin_room_layout", "move_room_group", resp)
+
+    async with client:
+        await client.admin_move_room_group("g1")
+
+    sent = mock.call_args.args[0]
+    assert sent.group_id == "g1"
+    assert not sent.HasField("before_group_id")
+
+
+async def test_admin_move_sidebar_item(client):
+    resp = room_layout_pb2.MoveSidebarItemResponse()
+    resp.group.id = "g1"
+    resp.group.name = "General"
+    mock = _mock_method(client, "admin_room_layout", "move_sidebar_item", resp)
+
+    async with client:
+        group = await client.admin_move_sidebar_item(
+            (AdminRoomLayoutItemKind.SIDEBAR_LINK, "sl1"),
+            "g1",
+            before=(AdminRoomLayoutItemKind.ROOM, "r1"),
+        )
+
+    assert group.id == "g1"
+    sent = mock.call_args.args[0]
+    item_kind = room_layout_pb2.AdminRoomLayoutItemKind
+    assert sent.item.id == "sl1"
+    assert sent.item.kind == item_kind.ADMIN_ROOM_LAYOUT_ITEM_KIND_SIDEBAR_LINK
+    assert sent.before.id == "r1"
+    assert sent.before.kind == item_kind.ADMIN_ROOM_LAYOUT_ITEM_KIND_ROOM
+    assert sent.group_id == "g1"
+
+
 async def test_admin_update_server_config(client):
     resp = admin_server_pb2.UpdateServerConfigResponse()
     resp.config.server_name = "MyServer"

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác