diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e972fe6795ce..c023493884b5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,3 +16,11 @@ guidance. local validation. - Prefer focused changes and the smallest existing validation that covers them. Do not claim a check passed unless it was run successfully. + +## Learnings + +When addressing PR feedback, reply in each addressed review thread with the +change or deliberate no-change decision and validation evidence. After pushing, +recheck previously addressed threads and resolve those whose fixes are now +published and verified. Do not leave delivered fixes open, or resolve a thread +merely because a fix exists locally; explain and retain unresolved blockers. diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/CHANGELOG.md b/sdk/webpubsub/azure-messaging-webpubsubchatservice/CHANGELOG.md index b2d8f64338bc..823989596dd2 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/CHANGELOG.md +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/CHANGELOG.md @@ -1,5 +1,25 @@ # Release History +## 1.0.0b2 (2026-11-13) + +### Features Added + + - Added topic lifecycle operations (create or replace, list, get, update, and soft delete). Creation or replacement accepts `ChatTopicCreateOrReplace`, which requires `title` and accepts optional `state` for replacement; updates accept `ChatTopicUpdate`, whose fields are optional. + - Added room, user, and user-room collection operations + +### Other Changes + + - Changed the default service API version from `2026-02-01-preview` to `2026-08-01-preview` for both existing and newly added Chat operations + - Clarified that message IDs are pagination cursors for `list_messages`. The pager follows the service's `nextLink`; resume with `by_page(continuation_token=...)`. + +### Breaking Changes + + - Renamed request-body parameters from `resource` to `role`, `room`, `member`, `user`, `topic`, or `message`, matching the operation's resource. Positional calls are unchanged. + - Renamed `ChatConversation.parent_room` to `room_id`. The JSON field remains `parentRoom`. + - Removed the `continuation_token_parameter` argument from listing operations. Resume paging with `by_page(continuation_token=...)` on the returned `ItemPaged` or `AsyncItemPaged` instead. + - Create or replace operations now accept dedicated `ChatRoleCreateOrReplace`, `ChatRoomCreateOrReplace`, `ChatRoomMemberCreateOrReplace`, and `ChatUserCreateOrReplace` (including `HumanChatUserCreateOrReplace`) request models instead of resource response models. + - Message updates accept `ChatMessageUpdate`, whose fields are optional, instead of `ChatMessage`. Resource response model names are unchanged. + ## 1.0.0b1 (2026-10-08) ### Features Added diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md b/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md index 4991c0acda10..e9d91fd5a7c5 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md @@ -6,6 +6,8 @@ Use this client library in an application server to: - Create and manage chat roles and permissions. - Create users, rooms, and room memberships. +- List rooms, users, and the rooms associated with a user. +- Create, list, update, and soft delete room topics. - Get room conversations and query persisted message history. - Update and delete persisted messages. - Generate client access credentials for Chat WebSocket clients. @@ -15,7 +17,7 @@ Use this client library in an application server to: | [Package (PyPI)][package] | [API reference documentation][api_reference] | [Product documentation][product_docs] -| [Samples][samples] +| [Examples](#examples) | [Changelog][changelog] ## Getting started @@ -106,15 +108,9 @@ For more information, see [Authenticate Azure-hosted Python applications][azure_ ## Key concepts -### Client - -`WebPubSubChatServiceClient` is the entry point for managing Chat resources in one Web PubSub hub. Create one client for each endpoint and hub combination. The client can be used as a context manager and is safe to reuse for multiple operations. - -The asynchronous client is available from the `azure.messaging.webpubsubchatservice.aio` namespace. - ### Hub -A hub is a logical collection of WebSocket connections. A standard hub offers event-based real-time messaging through the Web PubSub subprotocol or a custom subprotocol. A chat hub adds built-in rooms, member management, message persistence, and chat-specific APIs. +A hub is a logical collection of WebSocket connections. A chat hub adds built-in rooms, member management, message persistence, and chat-specific APIs. This SDK applies only to chat hubs. Chat must be enabled on the target hub before the SDK can manage roles, users, rooms, members, conversations, or messages. @@ -126,7 +122,7 @@ User roles control operations such as creating rooms. Room roles control what a ### User -A user represents an application identity that can send and receive messages. In the service API, a user is identified by a user ID and assigned a user role. A human user also has a nickname. Client access credentials associate WebSocket connections with a user ID. +A user represents an identity participating in chat, such as a human end user or an application. Client access credentials associate WebSocket connections with a user ID. ### Room @@ -138,13 +134,52 @@ A room member represents a user added to a room. Membership controls which users ### Conversation and message history -A conversation is a message thread that belongs to a room. Every room has a default conversation and can contain multiple conversations. +A conversation is an ordered message stream within a room. Every room has a default conversation, and additional named conversations can be created through topics. Messages sent to a conversation are delivered in real time to the room's connected members. The chat service manages ordering and persistence, allowing members to load message history after reconnecting or joining later. The service client can list, update, and delete persisted messages. +`ChatConversation.room_id` identifies the room containing the conversation. +`list_messages` returns messages from latest to earliest, using message IDs as +pagination cursors. The returned pager follows the service's `nextLink` +automatically. To resume a page iteration, pass its continuation token to +`by_page(continuation_token=...)`, not to `list_messages` itself. + +### Topic + +A topic represents a named conversation within a room. Topics can be created, +renamed, archived, and soft deleted independently of the room's default conversation. + ## Examples -The following sections show common scenarios. See the [package samples][samples] for complete synchronous and asynchronous programs. +The async snippet +uses a client from `azure.messaging.webpubsubchatservice.aio`. +Replace placeholders such as `` with your application's resource identifiers. + +### Request-body styles + +You can supply a JSON request body in three ways: + +- **Model-based:** use classes from `azure.messaging.webpubsubchatservice.models` + with Python-style attributes and SDK-managed serialization. +- **Typed dictionary:** use the `TypedDict` definitions from + `azure.messaging.webpubsubchatservice.types` for static checking of dictionaries + that use JSON field names. +- **Plain dictionary:** pass a dictionary using JSON field names. + +Model-based usage is recommended for getting started and is used throughout the +examples below. Dictionaries are useful when your application already works with +JSON-shaped data. All three styles call the same client methods; typed dictionaries +are ordinary dictionaries at runtime and do not add runtime validation. See the +[examples guide](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md#request-body-styles) +for a comparison and examples of each style. + +Explore the examples: + +- [Model-based and scenario examples][examples], including complete synchronous and + asynchronous programs. +- [Model, typed-dictionary, and plain-dictionary comparison](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py). +- [Complete operation index](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md#complete-operation-index), + linking to handwritten examples for each service operation. ### Generate client access credentials @@ -153,99 +188,293 @@ Generate credentials that a Chat WebSocket client can use to connect as a specif ```python -import os -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -connection_string = os.environ["WPS_CHAT_CONNECTION_STRING"] -with WebPubSubChatServiceClient.from_connection_string( - connection_string, - os.environ.get("WPS_CHAT_HUB", "test_hub"), -) as client: - access = client.get_client_access_token(user_id="sample-user") - # Give access["url"] to the intended client to connect; it includes the access token. - # Print only the token-free base URL here. Do not log access["url"]. - print(access["baseUrl"]) +access = client.get_client_access_token(user_id="sample-user") +# Give access["url"] to the intended client to connect; it includes the access token. +# Print only the token-free base URL here. Do not log access["url"]. +print(access["baseUrl"]) ``` The returned URL contains an access token. Send it only to the intended client, and do not log or persist it in production. -### Create and list roles +### Create a role + +Create operations use dedicated request models such as `ChatRoleCreateOrReplace` and +`ChatRoomCreateOrReplace`. PATCH operations use `ChatTopicUpdate` and `ChatMessageUpdate`, +whose fields can be omitted to leave existing values unchanged. The operations +return resource models such as `ChatRole`, `ChatRoom`, and `ChatTopic`, which +include service-assigned identifiers and ETags. + +Use the `ChatRoleCreateOrReplace` model and `ChatPermission` enum instead of a dictionary with +permission string literals. + + ```python -from azure.messaging.webpubsubchatservice.models import ChatPermission, ChatRole +from azure.messaging.webpubsubchatservice.models import ChatPermission, ChatRoleCreateOrReplace -role_name = "user.moderator" -try: - role = client.create_or_replace_role( - role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), - ) - print(role.name, role.permissions) +role = client.create_or_replace_role( + "user.example", + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), +) +print(role.name, role.permissions) +``` + + - for listed_role in client.list_roles(): - print(listed_role.name) -finally: - client.delete_role(role_name) +See the [synchronous role example](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py) +and [asynchronous role example](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py) +for runnable model-and-enum examples. The selected role is created or replaced +and remains in the hub; these examples do not create users or rooms. + +### List roles + + + +```python +for role in client.list_roles(): + print(role.name) ``` -### Create a user, room, and room membership + + +### Create a user + +The user role `user.room_creator` must already exist. + + ```python -from azure.messaging.webpubsubchatservice.models import ( - ChatPermission, - ChatRole, - ChatRoom, - ChatRoomMember, - HumanChatUser, -) +from azure.messaging.webpubsubchatservice.models import HumanChatUserCreateOrReplace -client.create_or_replace_role( - "user.room_creator", - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), -) -client.create_or_replace_role( - "room.contributor", - ChatRole(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE]), -) -client.create_or_replace_user( +user = client.create_or_replace_user( "alice", - HumanChatUser(nickname="Alice", role_name="user.room_creator"), + HumanChatUserCreateOrReplace(nickname="Alice", role_name="user.room_creator"), ) -room = client.create_or_replace_room("general", ChatRoom(title="General")) +print(user.id, user.nickname) +``` + + + +### Create a room + + + +```python +from azure.messaging.webpubsubchatservice.models import ChatRoomCreateOrReplace + +room = client.create_or_replace_room("general", ChatRoomCreateOrReplace(title="General")) +print(room.id, room.default_conversation) +``` + + + +### Add a room member + +The room `general`, user `alice`, and room role `room.contributor` must already exist. + + + +```python +from azure.messaging.webpubsubchatservice.models import ChatRoomMemberCreateOrReplace + member = client.create_or_replace_room_member( - room.id, + "general", "alice", - ChatRoomMember(role_name="room.contributor"), + ChatRoomMemberCreateOrReplace(role_name="room.contributor"), ) print(member.user_id, member.role_name) ``` -Delete dependent resources in reverse order when they are no longer needed: room, user, and then roles. + ### List persisted messages +Replace `` with an existing conversation ID. + + + ```python -room = client.get_room("general") -for message in client.list_messages(room.default_conversation): +for message in client.list_messages(""): print(message.id, message.created_by, message.content.text) ``` + + +### Manage room topics + +Replace `` with an existing room ID and `` with the topic ID. + +#### Create a topic + +`ChatTopicCreateOrReplace` requires a title. When replacing an existing topic, +you can also supply `state`, for example +`ChatTopicCreateOrReplace(title="Announcements", state=ChatTopicState.ARCHIVED)`. +Import `ChatTopicState` from `azure.messaging.webpubsubchatservice.models`. +Omit `state` when creating a topic. + + + +```python +from azure.messaging.webpubsubchatservice.models import ChatTopicCreateOrReplace + +topic = client.create_or_replace_topic( + "", + "", + ChatTopicCreateOrReplace(title="Announcements"), +) +print(topic.id, topic.etag) +``` + + + +#### List topics + + + +```python +for topic in client.list_topics("", max_page_size=10): + print(topic.id, topic.title, topic.conversation_id) +``` + + + +#### Get a topic + + + +```python +topic = client.get_topic("", "") +print(topic.id, topic.title, topic.state, topic.etag) +``` + + + +#### Archive a topic + +Replace `` with the ETag returned by a previous create or get +operation, preserving its quotes. The update below changes only `state`; the existing title is left +unchanged. To change only the title, use `ChatTopicUpdate(title="New title")`. + + + +```python +from azure.core import MatchConditions +from azure.messaging.webpubsubchatservice.models import ChatTopicUpdate, ChatTopicState + +topic = client.update_topic( + "", + "", + ChatTopicUpdate(state=ChatTopicState.ARCHIVED), + etag='""', + match_condition=MatchConditions.IfNotModified, +) +print(topic.id, topic.state, topic.etag) +``` + + + +#### Soft delete a topic + +Replace `` with the current ETag, including any change from an update. + + + +```python +from azure.core import MatchConditions + +client.delete_topic( + "", + "", + etag='""', + match_condition=MatchConditions.IfNotModified, +) +``` + + + +Use the topic's `conversation_id` with `get_conversation` and `list_messages`. + +For conditional updates and deletes, pass the current ETag with +`MatchConditions.IfNotModified`. A stale ETag prevents overwriting a topic +that another caller has changed. Deleting a topic is a soft delete. + +For individual API calls, see the +[operation examples](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md#complete-operation-index). + +### List rooms and users + +#### List rooms + + + +```python +for room in client.list_rooms(max_page_size=10): + print(room.id, room.title) +``` + + + +#### List users + + + +```python +for user in client.list_users(max_page_size=10): + print(user.id, user.kind) +``` + + + +#### List rooms for a user + +Replace `` with an existing user ID. + + + +```python +for room in client.list_rooms_for_user(""): + print(room.id, room.title) +``` + + + +For basic list calls, see the functions in +[the README snippet sources](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py). +Listing rooms for a user requires an existing user ID. + +Collection iterators automatically fetch subsequent pages. + ### Use the asynchronous client +Run this snippet inside an async function with an authenticated async `client`; see +[the asynchronous client access example](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py) +for the complete program. + + + ```python -from azure.identity.aio import DefaultAzureCredential -from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient +access = await client.get_client_access_token(user_id="sample-user") +# Give access["url"] to the intended client to connect; it includes the access token. +# Print only the token-free base URL here. Do not log access["url"]. +print(access["baseUrl"]) +``` -credential = DefaultAzureCredential() -client = WebPubSubChatServiceClient(endpoint, hub, credential) -try: - async for role in client.list_roles(): - print(role.name) -finally: - await client.close() - await credential.close() + + +### Run the examples + +Set `WPS_CHAT_CONNECTION_STRING` and optionally `WPS_CHAT_HUB` (default: `test_hub`). +The role examples use `user.example`; replace it with a role name reserved for +the example. An existing role with that name is replaced. +Install `aiohttp` to run async examples. + +From the package directory, run, for example: + +```bash +python samples/sample_roles_and_permissions.py +python samples/sample_roles_and_permissions_async.py ``` ## Troubleshooting @@ -254,21 +483,34 @@ finally: Service operations raise `HttpResponseError` or a more specific subclass when a request fails: +The [service error example](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_errors.py) reads an existing role, +prints the status on failure, and re-raises the error. Replace `user.example` +with the role name to read. + + + ```python from azure.core.exceptions import HttpResponseError try: - room = client.get_room("room-id") + role = client.get_role("user.example") + print(role.name, role.permissions) except HttpResponseError as error: print(f"Chat service request failed with status {error.status_code}") + raise ``` + + ### Logging This library uses the standard Python [logging][python_logging] library. Enable HTTP logging for a client by passing `logging_enable=True`: + + ```python import logging +import os import sys from azure.identity import DefaultAzureCredential @@ -278,36 +520,34 @@ logger = logging.getLogger("azure") logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler(stream=sys.stdout)) -client = WebPubSubChatServiceClient( - endpoint, - hub, - DefaultAzureCredential(), - logging_enable=True, -) +with DefaultAzureCredential() as credential: + with WebPubSubChatServiceClient( + os.environ["WPS_CHAT_ENDPOINT"], + os.environ.get("WPS_CHAT_HUB", "test_hub"), + credential, + logging_enable=True, + ) as client: + print(type(client).__name__) ``` -HTTP logs can contain sensitive information. Do not enable detailed logging in production without reviewing how logs are collected and protected. For more information, see [Configure logging in the Azure SDK for Python][azure_sdk_logging]. - -### Authentication and authorization - -- Confirm that the endpoint and hub name identify the Web PubSub resource and Chat-enabled hub you intend to use. -- For Microsoft Entra ID, confirm that the principal has an appropriate Web PubSub data-plane role and that role assignment propagation has completed. -- Connection-string and access-key authentication are unavailable when local authentication is disabled on the Web PubSub resource. - -### Message history + -If a newly sent message does not appear immediately, confirm that the sending user is a room member with publish permission and that message history is enabled for the member's room role. +HTTP logs can contain sensitive information. Do not enable detailed logging in production without reviewing how logs are collected and protected. For more information, see [Configure logging in the Azure SDK for Python][azure_sdk_logging]. ## Next steps -Explore the [complete package samples][samples] to learn how to: +Explore the [README snippet sources](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py) and +[complete package examples][examples] to learn how to: - Authenticate with a connection string, access key, or Microsoft Entra ID. - Manage roles, permissions, users, rooms, and room members. - Generate client access credentials. - Query, update, and delete message history. +- Manage topic lifecycles and iterate over room and user collections. - Use synchronous and asynchronous clients. +See also the [request-body style comparison](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py). + ## Additional resources - [Azure Web PubSub documentation][webpubsub_docs] @@ -326,7 +566,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][code_of_con [package]: https://pypi.org/project/azure-messaging-webpubsubchatservice/ [api_reference]: https://learn.microsoft.com/python/api/overview/azure/messaging-webpubsubchatservice-readme?view=azure-python-preview [product_docs]: https://learn.microsoft.com/azure/azure-web-pubsub/chat-overview -[samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples +[examples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples [changelog]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/CHANGELOG.md [azure_sub]: https://azure.microsoft.com/free/ [webpubsub_docs]: https://learn.microsoft.com/azure/azure-web-pubsub/ diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/_metadata.json b/sdk/webpubsub/azure-messaging-webpubsubchatservice/_metadata.json index 46cdcde90533..317a1629c2c5 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/_metadata.json +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/_metadata.json @@ -1,6 +1,6 @@ { - "apiVersion": "2026-02-01-preview", + "apiVersion": "2026-08-01-preview", "apiVersions": { - "WebPubSubChat": "2026-02-01-preview" + "WebPubSubChat": "2026-08-01-preview" } } \ No newline at end of file diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.md b/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.md index 4e1036da346a..16ea26a56873 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.md +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.md @@ -33,7 +33,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_role( self, role_name: str, - resource: ChatRole, + role: ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -45,7 +45,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_role( self, role_name: str, - resource: ChatRole, + role: ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -57,7 +57,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_role( self, role_name: str, - resource: IO[bytes], + role: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -69,7 +69,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_room( self, room_id: str, - resource: ChatRoom, + room: ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -81,7 +81,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_room( self, room_id: str, - resource: ChatRoom, + room: ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -93,7 +93,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_room( self, room_id: str, - resource: IO[bytes], + room: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -106,7 +106,7 @@ namespace azure.messaging.webpubsubchatservice self, room_id: str, user_id: str, - resource: ChatRoomMember, + member: ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -119,7 +119,7 @@ namespace azure.messaging.webpubsubchatservice self, room_id: str, user_id: str, - resource: ChatRoomMember, + member: ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -132,7 +132,7 @@ namespace azure.messaging.webpubsubchatservice self, room_id: str, user_id: str, - resource: IO[bytes], + member: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -140,11 +140,50 @@ namespace azure.messaging.webpubsubchatservice **kwargs: Any ) -> ChatRoomMember: ... + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + @overload def create_or_replace_user( self, user_id: str, - resource: ChatUser, + user: ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -156,7 +195,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_user( self, user_id: str, - resource: ChatUser, + user: ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -168,7 +207,7 @@ namespace azure.messaging.webpubsubchatservice def create_or_replace_user( self, user_id: str, - resource: IO[bytes], + user: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -218,6 +257,18 @@ namespace azure.messaging.webpubsubchatservice **kwargs: Any ) -> None: ... + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'topic_id', 'etag', 'match_condition']}, api_versions_list=['2026-08-01-preview']) + def delete_topic( + self, + room_id: str, + topic_id: str, + *, + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> None: ... + @distributed_trace def delete_user( self, @@ -258,6 +309,15 @@ namespace azure.messaging.webpubsubchatservice **kwargs: Any ) -> ChatRoom: ... + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'topic_id', 'accept']}, api_versions_list=['2026-08-01-preview']) + def get_topic( + self, + room_id: str, + topic_id: str, + **kwargs: Any + ) -> ChatTopic: ... + @distributed_trace def get_user( self, @@ -280,7 +340,6 @@ namespace azure.messaging.webpubsubchatservice def list_roles( self, *, - continuation_token_parameter: Optional[str] = ..., max_page_size: Optional[int] = ..., **kwargs: Any ) -> ItemPaged[ChatRole]: ... @@ -290,11 +349,48 @@ namespace azure.messaging.webpubsubchatservice self, room_id: str, *, - continuation_token_parameter: Optional[str] = ..., max_page_size: Optional[int] = ..., **kwargs: Any ) -> ItemPaged[ChatRoomMember]: ... + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_rooms( + self, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> ItemPaged[ChatRoom]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'user_id', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_rooms_for_user( + self, + user_id: str, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> ItemPaged[ChatUserRoom]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_topics( + self, + room_id: str, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> ItemPaged[ChatTopic]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_users( + self, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> ItemPaged[ChatUser]: ... + def send_request( self, request: HttpRequest, @@ -308,7 +404,7 @@ namespace azure.messaging.webpubsubchatservice self, conversation_id: str, message_id: str, - resource: ChatMessage, + message: ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -321,7 +417,7 @@ namespace azure.messaging.webpubsubchatservice self, conversation_id: str, message_id: str, - resource: ChatMessage, + message: ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -334,7 +430,7 @@ namespace azure.messaging.webpubsubchatservice self, conversation_id: str, message_id: str, - resource: IO[bytes], + message: IO[bytes], *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -342,6 +438,45 @@ namespace azure.messaging.webpubsubchatservice **kwargs: Any ) -> ChatMessage: ... + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + namespace azure.messaging.webpubsubchatservice.aio @@ -371,7 +506,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_role( self, role_name: str, - resource: ChatRole, + role: ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -383,7 +518,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_role( self, role_name: str, - resource: ChatRole, + role: ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -395,7 +530,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_role( self, role_name: str, - resource: IO[bytes], + role: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -407,7 +542,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_room( self, room_id: str, - resource: ChatRoom, + room: ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -419,7 +554,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_room( self, room_id: str, - resource: ChatRoom, + room: ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -431,7 +566,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_room( self, room_id: str, - resource: IO[bytes], + room: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -444,7 +579,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, room_id: str, user_id: str, - resource: ChatRoomMember, + member: ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -457,7 +592,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, room_id: str, user_id: str, - resource: ChatRoomMember, + member: ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -470,7 +605,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, room_id: str, user_id: str, - resource: IO[bytes], + member: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -478,11 +613,50 @@ namespace azure.messaging.webpubsubchatservice.aio **kwargs: Any ) -> ChatRoomMember: ... + @overload + async def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + async def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + async def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + @overload async def create_or_replace_user( self, user_id: str, - resource: ChatUser, + user: ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -494,7 +668,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_user( self, user_id: str, - resource: ChatUser, + user: ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -506,7 +680,7 @@ namespace azure.messaging.webpubsubchatservice.aio async def create_or_replace_user( self, user_id: str, - resource: IO[bytes], + user: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = ..., @@ -556,6 +730,18 @@ namespace azure.messaging.webpubsubchatservice.aio **kwargs: Any ) -> None: ... + @distributed_trace_async + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'topic_id', 'etag', 'match_condition']}, api_versions_list=['2026-08-01-preview']) + async def delete_topic( + self, + room_id: str, + topic_id: str, + *, + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> None: ... + @distributed_trace_async async def delete_user( self, @@ -596,6 +782,15 @@ namespace azure.messaging.webpubsubchatservice.aio **kwargs: Any ) -> ChatRoom: ... + @distributed_trace_async + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'topic_id', 'accept']}, api_versions_list=['2026-08-01-preview']) + async def get_topic( + self, + room_id: str, + topic_id: str, + **kwargs: Any + ) -> ChatTopic: ... + @distributed_trace_async async def get_user( self, @@ -618,7 +813,6 @@ namespace azure.messaging.webpubsubchatservice.aio def list_roles( self, *, - continuation_token_parameter: Optional[str] = ..., max_page_size: Optional[int] = ..., **kwargs: Any ) -> AsyncItemPaged[ChatRole]: ... @@ -628,11 +822,48 @@ namespace azure.messaging.webpubsubchatservice.aio self, room_id: str, *, - continuation_token_parameter: Optional[str] = ..., max_page_size: Optional[int] = ..., **kwargs: Any ) -> AsyncItemPaged[ChatRoomMember]: ... + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_rooms( + self, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> AsyncItemPaged[ChatRoom]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'user_id', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_rooms_for_user( + self, + user_id: str, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> AsyncItemPaged[ChatUserRoom]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'room_id', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_topics( + self, + room_id: str, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> AsyncItemPaged[ChatTopic]: ... + + @distributed_trace + @api_version_validation(method_added_on='2026-08-01-preview', params_added_on={'2026-08-01-preview': ['api_version', 'hub', 'max_page_size', 'accept']}, api_versions_list=['2026-08-01-preview']) + def list_users( + self, + *, + max_page_size: Optional[int] = ..., + **kwargs: Any + ) -> AsyncItemPaged[ChatUser]: ... + def send_request( self, request: HttpRequest, @@ -646,7 +877,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, conversation_id: str, message_id: str, - resource: ChatMessage, + message: ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -659,7 +890,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, conversation_id: str, message_id: str, - resource: ChatMessage, + message: ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -672,7 +903,7 @@ namespace azure.messaging.webpubsubchatservice.aio self, conversation_id: str, message_id: str, - resource: IO[bytes], + message: IO[bytes], *, content_type: str = "application/merge-patch+json", etag: Optional[str] = ..., @@ -680,19 +911,58 @@ namespace azure.messaging.webpubsubchatservice.aio **kwargs: Any ) -> ChatMessage: ... + @overload + async def update_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + async def update_topic( + self, + room_id: str, + topic_id: str, + topic: ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + + @overload + async def update_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = ..., + match_condition: Optional[MatchConditions] = ..., + **kwargs: Any + ) -> ChatTopic: ... + namespace azure.messaging.webpubsubchatservice.models class azure.messaging.webpubsubchatservice.models.ChatConversation(_Model): etag: str id: str - parent_room: str + room_id: str @overload def __init__( self, *, - parent_room: str + room_id: str ) -> None: ... @overload @@ -718,6 +988,22 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.ChatMessageUpdate(_Model): + content: Optional[MessageContent] + created_by: Optional[str] + + @overload + def __init__( + self, + *, + content: Optional[MessageContent] = ..., + created_by: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.ChatPermission(str, Enum, metaclass=CaseInsensitiveEnumMeta): ROOM_HISTORY = "room.history" ROOM_INVITE = "room.invite" @@ -743,6 +1029,20 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace(_Model): + permissions: list[Union[str, ChatPermission]] + + @overload + def __init__( + self, + *, + permissions: list[Union[str, ChatPermission]] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.ChatRoom(_Model): default_conversation: str etag: str @@ -760,6 +1060,20 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace(_Model): + title: str + + @overload + def __init__( + self, + *, + title: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.ChatRoomMember(_Model): etag: str role_name: str @@ -776,6 +1090,79 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace(_Model): + role_name: str + + @overload + def __init__( + self, + *, + role_name: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.messaging.webpubsubchatservice.models.ChatTopic(_Model): + conversation_id: str + created_at: datetime + created_by: str + etag: str + id: str + state: Union[str, ChatTopicState] + title: str + + @overload + def __init__( + self, + *, + state: Union[str, ChatTopicState], + title: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace(_Model): + state: Optional[Union[str, ChatTopicState]] + title: str + + @overload + def __init__( + self, + *, + state: Optional[Union[str, ChatTopicState]] = ..., + title: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.messaging.webpubsubchatservice.models.ChatTopicState(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ARCHIVED = "Archived" + NORMAL = "Normal" + SOFT_DELETED = "SoftDeleted" + + + class azure.messaging.webpubsubchatservice.models.ChatTopicUpdate(_Model): + state: Optional[Union[str, ChatTopicState]] + title: Optional[str] + + @overload + def __init__( + self, + *, + state: Optional[Union[str, ChatTopicState]] = ..., + title: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.ChatUser(_Model): etag: str id: str @@ -794,10 +1181,43 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace(_Model): + kind: str + nickname: str + + @overload + def __init__( + self, + *, + kind: str, + nickname: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.ChatUserKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): HUMAN = "Human" + class azure.messaging.webpubsubchatservice.models.ChatUserRoom(_Model): + default_conversation: str + etag: str + id: str + title: str + + @overload + def __init__( + self, + *, + title: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.HumanChatUser(ChatUser, discriminator='Human'): etag: str id: str @@ -817,6 +1237,23 @@ namespace azure.messaging.webpubsubchatservice.models def __init__(self, mapping: Mapping[str, Any]) -> None: ... + class azure.messaging.webpubsubchatservice.models.HumanChatUserCreateOrReplace(ChatUserCreateOrReplace, discriminator='Human'): + kind: Literal[ChatUserKind.HUMAN] + nickname: str + role_name: str + + @overload + def __init__( + self, + *, + nickname: str, + role_name: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + class azure.messaging.webpubsubchatservice.models.MessageContent(_Model): binary: Optional[bytes] text: Optional[str] @@ -835,56 +1272,46 @@ namespace azure.messaging.webpubsubchatservice.models namespace azure.messaging.webpubsubchatservice.types - class azure.messaging.webpubsubchatservice.types.ChatMessage(TypedDict, total=False): - key "content": Required[MessageContent] - key "createdAt": Required[str] - key "createdBy": Required[str] - key "etag": Required[str] - key "id": Required[str] + class azure.messaging.webpubsubchatservice.types.ChatMessageUpdate(TypedDict, total=False): + key "content": ForwardRef('MessageContent', module='types') + key "createdBy": str content: MessageContent - createdAt: str createdBy: str - etag: str - id: str - class azure.messaging.webpubsubchatservice.types.ChatRole(TypedDict, total=False): - key "etag": Required[str] - key "name": Required[str] + class azure.messaging.webpubsubchatservice.types.ChatRoleCreateOrReplace(TypedDict, total=False): key "permissions": Required[list[Union[str, ChatPermission]]] - etag: str - name: str permissions: list[Union[str, ChatPermission]] - class azure.messaging.webpubsubchatservice.types.ChatRoom(TypedDict, total=False): - key "defaultConversation": Required[str] - key "etag": Required[str] - key "id": Required[str] + class azure.messaging.webpubsubchatservice.types.ChatRoomCreateOrReplace(TypedDict, total=False): key "title": Required[str] - defaultConversation: str - etag: str - id: str title: str - class azure.messaging.webpubsubchatservice.types.ChatRoomMember(TypedDict, total=False): - key "etag": Required[str] + class azure.messaging.webpubsubchatservice.types.ChatRoomMemberCreateOrReplace(TypedDict, total=False): key "roleName": Required[str] - key "userId": Required[str] - etag: str roleName: str - userId: str - class azure.messaging.webpubsubchatservice.types.ChatUser(TypedDict, total=False): - key "etag": Required[str] - key "id": Required[str] + class azure.messaging.webpubsubchatservice.types.ChatTopicCreateOrReplace(TypedDict, total=False): + key "state": Union[str, ChatTopicState] + key "title": Required[str] + state: Union[str, ChatTopicState] + title: str + + + class azure.messaging.webpubsubchatservice.types.ChatTopicUpdate(TypedDict, total=False): + key "state": Union[str, ChatTopicState] + key "title": str + state: Union[str, ChatTopicState] + title: str + + + class azure.messaging.webpubsubchatservice.types.ChatUserCreateOrReplace(TypedDict, total=False): key "kind": Required[Literal[ChatUserKind.HUMAN]] key "nickname": Required[str] key "roleName": Required[str] - etag: str - id: str kind: Literal[ChatUserKind.HUMAN] nickname: str roleName: str @@ -894,14 +1321,10 @@ namespace azure.messaging.webpubsubchatservice.types HUMAN = "Human" - class azure.messaging.webpubsubchatservice.types.HumanChatUser(TypedDict, total=False): - key "etag": Required[str] - key "id": Required[str] + class azure.messaging.webpubsubchatservice.types.HumanChatUserCreateOrReplace(TypedDict, total=False): key "kind": Required[Literal[ChatUserKind.HUMAN]] key "nickname": Required[str] key "roleName": Required[str] - etag: str - id: str kind: Literal[ChatUserKind.HUMAN] nickname: str roleName: str diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.metadata.yml b/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.metadata.yml index b80e4c3b6af7..6de17fb462c2 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.metadata.yml +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/api.metadata.yml @@ -1,3 +1,4 @@ -apiMdSha256: 0e0114ad50923e1ebecf18b76ada92c10de2d7e1318c6428a108f33e95f77593 +apiMdSha256: bc6ec089811cdbda7c3ae09f1d6b38e2a1ab37b5d0f31b69258b7832bd31527f +packageVersion: 1.0.0b2 parserVersion: 0.3.31 -pythonVersion: 3.12.13 +pythonVersion: 3.12.10 diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/apiview-properties.json b/sdk/webpubsub/azure-messaging-webpubsubchatservice/apiview-properties.json index ff91009b463c..d7e789e6c120 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/apiview-properties.json +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/apiview-properties.json @@ -3,13 +3,24 @@ "CrossLanguageDefinitionId": { "azure.messaging.webpubsubchatservice.models.ChatConversation": "WebPubSubChat.ChatConversation", "azure.messaging.webpubsubchatservice.models.ChatMessage": "WebPubSubChat.ChatMessage", + "azure.messaging.webpubsubchatservice.models.ChatMessageUpdate": "ClientCustomizations.ChatMessageUpdate", "azure.messaging.webpubsubchatservice.models.ChatRole": "WebPubSubChat.ChatRole", + "azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace": "ClientCustomizations.ChatRoleCreateOrReplace", "azure.messaging.webpubsubchatservice.models.ChatRoom": "WebPubSubChat.ChatRoom", + "azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace": "ClientCustomizations.ChatRoomCreateOrReplace", "azure.messaging.webpubsubchatservice.models.ChatRoomMember": "WebPubSubChat.ChatRoomMember", + "azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace": "ClientCustomizations.ChatRoomMemberCreateOrReplace", + "azure.messaging.webpubsubchatservice.models.ChatTopic": "WebPubSubChat.ChatTopic", + "azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace": "ClientCustomizations.ChatTopicCreateOrReplace", + "azure.messaging.webpubsubchatservice.models.ChatTopicUpdate": "ClientCustomizations.ChatTopicUpdate", "azure.messaging.webpubsubchatservice.models.ChatUser": "WebPubSubChat.ChatUser", + "azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace": "ClientCustomizations.ChatUserCreateOrReplace", + "azure.messaging.webpubsubchatservice.models.ChatUserRoom": "WebPubSubChat.ChatUserRoom", "azure.messaging.webpubsubchatservice.models.HumanChatUser": "WebPubSubChat.HumanChatUser", + "azure.messaging.webpubsubchatservice.models.HumanChatUserCreateOrReplace": "ClientCustomizations.HumanChatUserCreateOrReplace", "azure.messaging.webpubsubchatservice.models.MessageContent": "WebPubSubChat.MessageContent", "azure.messaging.webpubsubchatservice.models.ChatPermission": "WebPubSubChat.ChatPermission", + "azure.messaging.webpubsubchatservice.models.ChatTopicState": "WebPubSubChat.ChatTopicState", "azure.messaging.webpubsubchatservice.models.ChatUserKind": "WebPubSubChat.ChatUserKind", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.get_conversation": "WebPubSubChat.WebPubSubChatServiceClient.getConversation", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.get_conversation": "WebPubSubChat.WebPubSubChatServiceClient.getConversation", @@ -17,34 +28,50 @@ "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_messages": "WebPubSubChat.WebPubSubChatServiceClient.listMessages", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_message": "WebPubSubChat.WebPubSubChatServiceClient.deleteMessage", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_message": "WebPubSubChat.WebPubSubChatServiceClient.deleteMessage", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.update_message": "WebPubSubChat.WebPubSubChatServiceClient.updateMessage", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.update_message": "WebPubSubChat.WebPubSubChatServiceClient.updateMessage", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_roles": "WebPubSubChat.WebPubSubChatServiceClient.listRoles", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_roles": "WebPubSubChat.WebPubSubChatServiceClient.listRoles", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.update_message": "Customizations.WebPubSubChatServiceClient.updateMessage", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.update_message": "Customizations.WebPubSubChatServiceClient.updateMessage", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_roles": "Customizations.WebPubSubChatServiceClient.listRoles", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_roles": "Customizations.WebPubSubChatServiceClient.listRoles", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.get_role": "WebPubSubChat.WebPubSubChatServiceClient.getRole", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.get_role": "WebPubSubChat.WebPubSubChatServiceClient.getRole", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_role": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRole", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_role": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRole", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_role": "Customizations.WebPubSubChatServiceClient.createOrReplaceRole", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_role": "Customizations.WebPubSubChatServiceClient.createOrReplaceRole", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_role": "WebPubSubChat.WebPubSubChatServiceClient.deleteRole", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_role": "WebPubSubChat.WebPubSubChatServiceClient.deleteRole", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_room": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRoom", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_room": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRoom", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_rooms": "Customizations.WebPubSubChatServiceClient.listRooms", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_rooms": "Customizations.WebPubSubChatServiceClient.listRooms", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_room": "Customizations.WebPubSubChatServiceClient.createOrReplaceRoom", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_room": "Customizations.WebPubSubChatServiceClient.createOrReplaceRoom", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.get_room": "WebPubSubChat.WebPubSubChatServiceClient.getRoom", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.get_room": "WebPubSubChat.WebPubSubChatServiceClient.getRoom", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_room": "WebPubSubChat.WebPubSubChatServiceClient.deleteRoom", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_room": "WebPubSubChat.WebPubSubChatServiceClient.deleteRoom", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_room_members": "WebPubSubChat.WebPubSubChatServiceClient.listRoomMembers", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_room_members": "WebPubSubChat.WebPubSubChatServiceClient.listRoomMembers", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_room_member": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRoomMember", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_room_member": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceRoomMember", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_topics": "Customizations.WebPubSubChatServiceClient.listTopics", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_topics": "Customizations.WebPubSubChatServiceClient.listTopics", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_topic": "Customizations.WebPubSubChatServiceClient.createOrReplaceTopic", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_topic": "Customizations.WebPubSubChatServiceClient.createOrReplaceTopic", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.get_topic": "WebPubSubChat.WebPubSubChatServiceClient.getTopic", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.get_topic": "WebPubSubChat.WebPubSubChatServiceClient.getTopic", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.update_topic": "Customizations.WebPubSubChatServiceClient.updateTopic", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.update_topic": "Customizations.WebPubSubChatServiceClient.updateTopic", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_topic": "WebPubSubChat.WebPubSubChatServiceClient.deleteTopic", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_topic": "WebPubSubChat.WebPubSubChatServiceClient.deleteTopic", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_room_members": "Customizations.WebPubSubChatServiceClient.listRoomMembers", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_room_members": "Customizations.WebPubSubChatServiceClient.listRoomMembers", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_room_member": "Customizations.WebPubSubChatServiceClient.createOrReplaceRoomMember", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_room_member": "Customizations.WebPubSubChatServiceClient.createOrReplaceRoomMember", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_room_member": "WebPubSubChat.WebPubSubChatServiceClient.deleteRoomMember", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_room_member": "WebPubSubChat.WebPubSubChatServiceClient.deleteRoomMember", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_users": "Customizations.WebPubSubChatServiceClient.listUsers", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_users": "Customizations.WebPubSubChatServiceClient.listUsers", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.get_user": "WebPubSubChat.WebPubSubChatServiceClient.getUser", "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.get_user": "WebPubSubChat.WebPubSubChatServiceClient.getUser", - "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_user": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceUser", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_user": "WebPubSubChat.WebPubSubChatServiceClient.createOrReplaceUser", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.create_or_replace_user": "Customizations.WebPubSubChatServiceClient.createOrReplaceUser", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.create_or_replace_user": "Customizations.WebPubSubChatServiceClient.createOrReplaceUser", "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.delete_user": "WebPubSubChat.WebPubSubChatServiceClient.deleteUser", - "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_user": "WebPubSubChat.WebPubSubChatServiceClient.deleteUser" + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.delete_user": "WebPubSubChat.WebPubSubChatServiceClient.deleteUser", + "azure.messaging.webpubsubchatservice.WebPubSubChatServiceClient.list_rooms_for_user": "Customizations.WebPubSubChatServiceClient.listRoomsForUser", + "azure.messaging.webpubsubchatservice.aio.WebPubSubChatServiceClient.list_rooms_for_user": "Customizations.WebPubSubChatServiceClient.listRoomsForUser" }, - "CrossLanguageVersion": "dd550ec76602" + "CrossLanguageVersion": "7f7888447aa3" } \ No newline at end of file diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/assets.json b/sdk/webpubsub/azure-messaging-webpubsubchatservice/assets.json index cfeddc2beb3a..5cd82829d468 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/assets.json +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/webpubsub/azure-messaging-webpubsubchatservice", - "Tag": "python/webpubsub/azure-messaging-webpubsubchatservice_05cae67921" + "Tag": "python/webpubsub/azure-messaging-webpubsubchatservice_6a7c87de21" } diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_client.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_client.py index 8257c317d31c..88c902598b22 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_client.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_client.py @@ -40,7 +40,7 @@ class WebPubSubChatServiceClient( alpha-numeric characters or underscore. Required. :type hub: str :keyword api_version: The API version to use for this operation. Known values are - "2026-02-01-preview" and None. Default value is None. If not set, the operation's default API + "2026-08-01-preview" and None. Default value is None. If not set, the operation's default API version will be used. Note that overriding this default value may result in unsupported behavior. :paramtype api_version: str diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_configuration.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_configuration.py index 0a49b3f1374b..834deb1ccf70 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_configuration.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_configuration.py @@ -31,14 +31,14 @@ class WebPubSubChatServiceClientConfiguration: # pylint: disable=too-many-insta alpha-numeric characters or underscore. Required. :type hub: str :keyword api_version: The API version to use for this operation. Known values are - "2026-02-01-preview" and None. Default value is None. If not set, the operation's default API + "2026-08-01-preview" and None. Default value is None. If not set, the operation's default API version will be used. Note that overriding this default value may result in unsupported behavior. :paramtype api_version: str """ def __init__(self, endpoint: str, credential: "TokenCredential", hub: str, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2026-02-01-preview") + api_version: str = kwargs.pop("api_version", "2026-08-01-preview") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_operations/_operations.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_operations/_operations.py index b4b9bdd60061..45bd811f0b82 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_operations/_operations.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_operations/_operations.py @@ -35,6 +35,7 @@ from .._utils.model_base import SdkJSONEncoder, _deserialize from .._utils.serialization import Serializer from .._utils.utils import ClientMixinABC, prep_if_match, prep_if_none_match +from .._validation import api_version_validation T = TypeVar("T") ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, dict[str, Any]], Any]] @@ -49,7 +50,7 @@ def build_web_pub_sub_chat_service_get_conversation_request( # pylint: disable= _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -82,7 +83,7 @@ def build_web_pub_sub_chat_service_list_messages_request( # pylint: disable=nam _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -121,7 +122,7 @@ def build_web_pub_sub_chat_service_delete_message_request( # pylint: disable=na _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) # Construct URL _url = "/api/hubs/{hub}/chat/conversations/{conversationId}/messages/{messageId}" path_format_arguments = { @@ -159,7 +160,7 @@ def build_web_pub_sub_chat_service_update_message_request( # pylint: disable=na _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -190,12 +191,12 @@ def build_web_pub_sub_chat_service_update_message_request( # pylint: disable=na def build_web_pub_sub_chat_service_list_roles_request( # pylint: disable=name-too-long - hub: str, *, max_page_size: Optional[int] = None, continuation_token_parameter: Optional[str] = None, **kwargs: Any + hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -210,10 +211,6 @@ def build_web_pub_sub_chat_service_list_roles_request( # pylint: disable=name-t _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") if max_page_size is not None: _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") - if continuation_token_parameter is not None: - _params["continuationToken"] = _SERIALIZER.query( - "continuation_token_parameter", continuation_token_parameter, "str" - ) # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -227,7 +224,7 @@ def build_web_pub_sub_chat_service_get_role_request( # pylint: disable=name-too _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -260,7 +257,7 @@ def build_web_pub_sub_chat_service_create_or_replace_role_request( # pylint: di _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -300,7 +297,7 @@ def build_web_pub_sub_chat_service_delete_role_request( # pylint: disable=name- _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) # Construct URL _url = "/api/hubs/{hub}/chat/roles/{roleName}" path_format_arguments = { @@ -324,6 +321,34 @@ def build_web_pub_sub_chat_service_delete_role_request( # pylint: disable=name- return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) +def build_web_pub_sub_chat_service_list_rooms_request( # pylint: disable=name-too-long + hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if max_page_size is not None: + _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + def build_web_pub_sub_chat_service_create_or_replace_room_request( # pylint: disable=name-too-long room_id: str, hub: str, @@ -336,7 +361,7 @@ def build_web_pub_sub_chat_service_create_or_replace_room_request( # pylint: di _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -371,7 +396,7 @@ def build_web_pub_sub_chat_service_get_room_request( # pylint: disable=name-too _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -403,7 +428,7 @@ def build_web_pub_sub_chat_service_delete_room_request( # pylint: disable=name- _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) # Construct URL _url = "/api/hubs/{hub}/chat/rooms/{roomId}" path_format_arguments = { @@ -427,18 +452,193 @@ def build_web_pub_sub_chat_service_delete_room_request( # pylint: disable=name- return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) -def build_web_pub_sub_chat_service_list_room_members_request( # pylint: disable=name-too-long +def build_web_pub_sub_chat_service_list_topics_request( # pylint: disable=name-too-long + room_id: str, hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms/{roomId}/topics" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "roomId": _SERIALIZER.url("room_id", room_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if max_page_size is not None: + _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_web_pub_sub_chat_service_create_or_replace_topic_request( # pylint: disable=name-too-long room_id: str, + topic_id: str, hub: str, *, - max_page_size: Optional[int] = None, - continuation_token_parameter: Optional[str] = None, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms/{roomId}/topics/{topicId}" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "roomId": _SERIALIZER.url("room_id", room_id, "str"), + "topicId": _SERIALIZER.url("topic_id", topic_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + if_match = prep_if_match(etag, match_condition) + if if_match is not None: + _headers["If-Match"] = _SERIALIZER.header("if_match", if_match, "str") + if_none_match = prep_if_none_match(etag, match_condition) + if if_none_match is not None: + _headers["If-None-Match"] = _SERIALIZER.header("if_none_match", if_none_match, "str") + + return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_web_pub_sub_chat_service_get_topic_request( # pylint: disable=name-too-long + room_id: str, topic_id: str, hub: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms/{roomId}/topics/{topicId}" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "roomId": _SERIALIZER.url("room_id", room_id, "str"), + "topicId": _SERIALIZER.url("topic_id", topic_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_web_pub_sub_chat_service_update_topic_request( # pylint: disable=name-too-long + room_id: str, + topic_id: str, + hub: str, + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms/{roomId}/topics/{topicId}" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "roomId": _SERIALIZER.url("room_id", room_id, "str"), + "topicId": _SERIALIZER.url("topic_id", topic_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + if_match = prep_if_match(etag, match_condition) + if if_match is not None: + _headers["If-Match"] = _SERIALIZER.header("if_match", if_match, "str") + if_none_match = prep_if_none_match(etag, match_condition) + if if_none_match is not None: + _headers["If-None-Match"] = _SERIALIZER.header("if_none_match", if_none_match, "str") + + return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_web_pub_sub_chat_service_delete_topic_request( # pylint: disable=name-too-long + room_id: str, + topic_id: str, + hub: str, + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + # Construct URL + _url = "/api/hubs/{hub}/chat/rooms/{roomId}/topics/{topicId}" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "roomId": _SERIALIZER.url("room_id", room_id, "str"), + "topicId": _SERIALIZER.url("topic_id", topic_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if_match = prep_if_match(etag, match_condition) + if if_match is not None: + _headers["If-Match"] = _SERIALIZER.header("if_match", if_match, "str") + if_none_match = prep_if_none_match(etag, match_condition) + if if_none_match is not None: + _headers["If-None-Match"] = _SERIALIZER.header("if_none_match", if_none_match, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_web_pub_sub_chat_service_list_room_members_request( # pylint: disable=name-too-long + room_id: str, hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -454,10 +654,6 @@ def build_web_pub_sub_chat_service_list_room_members_request( # pylint: disable _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") if max_page_size is not None: _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") - if continuation_token_parameter is not None: - _params["continuationToken"] = _SERIALIZER.query( - "continuation_token_parameter", continuation_token_parameter, "str" - ) # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") @@ -478,7 +674,7 @@ def build_web_pub_sub_chat_service_create_or_replace_room_member_request( # pyl _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -520,7 +716,7 @@ def build_web_pub_sub_chat_service_delete_room_member_request( # pylint: disabl _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) # Construct URL _url = "/api/hubs/{hub}/chat/rooms/{roomId}/members/{userId}" path_format_arguments = { @@ -545,13 +741,41 @@ def build_web_pub_sub_chat_service_delete_room_member_request( # pylint: disabl return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) +def build_web_pub_sub_chat_service_list_users_request( # pylint: disable=name-too-long + hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/users" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if max_page_size is not None: + _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + def build_web_pub_sub_chat_service_get_user_request( # pylint: disable=name-too-long user_id: str, hub: str, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -584,7 +808,7 @@ def build_web_pub_sub_chat_service_create_or_replace_user_request( # pylint: di _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -624,7 +848,7 @@ def build_web_pub_sub_chat_service_delete_user_request( # pylint: disable=name- _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-02-01-preview")) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) # Construct URL _url = "/api/hubs/{hub}/chat/users/{userId}" path_format_arguments = { @@ -648,6 +872,35 @@ def build_web_pub_sub_chat_service_delete_user_request( # pylint: disable=name- return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) +def build_web_pub_sub_chat_service_list_rooms_for_user_request( # pylint: disable=name-too-long + user_id: str, hub: str, *, max_page_size: Optional[int] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-08-01-preview")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/hubs/{hub}/chat/users/{userId}/rooms" + path_format_arguments = { + "hub": _SERIALIZER.url("hub", hub, "str"), + "userId": _SERIALIZER.url("user_id", user_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + if max_page_size is not None: + _params["maxpagesize"] = _SERIALIZER.query("max_page_size", max_page_size, "int") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + def build_web_pub_sub_chat_service_generate_client_token_request( # pylint: disable=name-too-long hub: str, *, @@ -685,7 +938,7 @@ def build_web_pub_sub_chat_service_generate_client_token_request( # pylint: dis return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) -class _WebPubSubChatServiceClientOperationsMixin( +class _WebPubSubChatServiceClientOperationsMixin( # pylint: disable=too-many-public-methods ClientMixinABC[PipelineClient[HttpRequest, HttpResponse], WebPubSubChatServiceClientConfiguration] ): @@ -765,7 +1018,8 @@ def list_messages( ) -> ItemPaged["_models.ChatMessage"]: """Query messages in a conversation from latest to earliest. - Query messages in a conversation from latest to earliest. + Message IDs are used as pagination cursors. Follow the response nextLink, which contains the + message ID cursor, to retrieve the next page. :param conversation_id: Conversation identifier. Required. :type conversation_id: str @@ -946,7 +1200,7 @@ def update_message( self, conversation_id: str, message_id: str, - resource: _models.ChatMessage, + message: _models.ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -961,8 +1215,8 @@ def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatMessage + :param message: The resource instance. Required. + :type message: ~azure.messaging.webpubsubchatservice.models.ChatMessageUpdate :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -981,7 +1235,7 @@ def update_message( self, conversation_id: str, message_id: str, - resource: _types.ChatMessage, + message: _types.ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -996,8 +1250,8 @@ def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatMessage + :param message: The resource instance. Required. + :type message: ~azure.messaging.webpubsubchatservice.types.ChatMessageUpdate :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -1016,7 +1270,7 @@ def update_message( self, conversation_id: str, message_id: str, - resource: IO[bytes], + message: IO[bytes], *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -1031,8 +1285,8 @@ def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param message: The resource instance. Required. + :type message: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -1051,7 +1305,7 @@ def update_message( self, conversation_id: str, message_id: str, - resource: Union[_models.ChatMessage, _types.ChatMessage, IO[bytes]], + message: Union[_models.ChatMessageUpdate, _types.ChatMessageUpdate, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -1065,10 +1319,10 @@ def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Is either a ChatMessage type or a IO[bytes] type. + :param message: The resource instance. Is either a ChatMessageUpdate type or a IO[bytes] type. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatMessage or - ~azure.messaging.webpubsubchatservice.types.ChatMessage or IO[bytes] + :type message: ~azure.messaging.webpubsubchatservice.models.ChatMessageUpdate or + ~azure.messaging.webpubsubchatservice.types.ChatMessageUpdate or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1100,10 +1354,10 @@ def update_message( content_type = content_type or "application/merge-patch+json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(message, (IOBase, bytes)): + _content = message else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(message, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_update_message_request( conversation_id=conversation_id, @@ -1153,18 +1407,13 @@ def update_message( return deserialized # type: ignore @distributed_trace - def list_roles( - self, *, max_page_size: Optional[int] = None, continuation_token_parameter: Optional[str] = None, **kwargs: Any - ) -> ItemPaged["_models.ChatRole"]: + def list_roles(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> ItemPaged["_models.ChatRole"]: """Query roles in a hub. Query roles in a hub. :keyword max_page_size: The maximum number of result items per page. Default value is None. :paramtype max_page_size: int - :keyword continuation_token_parameter: Continuation token for pagination. Default value is - None. - :paramtype continuation_token_parameter: str :return: An iterator like instance of ChatRole :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRole] :raises ~azure.core.exceptions.HttpResponseError: @@ -1188,7 +1437,6 @@ def prepare_request(next_link=None): _request = build_web_pub_sub_chat_service_list_roles_request( hub=self._config.hub, max_page_size=max_page_size, - continuation_token_parameter=continuation_token_parameter, api_version=self._config.api_version, headers=_headers, params=_params, @@ -1320,7 +1568,7 @@ def get_role(self, role_name: str, **kwargs: Any) -> _models.ChatRole: def create_or_replace_role( self, role_name: str, - resource: _models.ChatRole, + role: _models.ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1333,8 +1581,8 @@ def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRole + :param role: The resource instance. Required. + :type role: ~azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1352,7 +1600,7 @@ def create_or_replace_role( def create_or_replace_role( self, role_name: str, - resource: _types.ChatRole, + role: _types.ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1365,8 +1613,8 @@ def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRole + :param role: The resource instance. Required. + :type role: ~azure.messaging.webpubsubchatservice.types.ChatRoleCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1384,7 +1632,7 @@ def create_or_replace_role( def create_or_replace_role( self, role_name: str, - resource: IO[bytes], + role: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1397,8 +1645,8 @@ def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param role: The resource instance. Required. + :type role: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -1416,7 +1664,7 @@ def create_or_replace_role( def create_or_replace_role( self, role_name: str, - resource: Union[_models.ChatRole, _types.ChatRole, IO[bytes]], + role: Union[_models.ChatRoleCreateOrReplace, _types.ChatRoleCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -1428,10 +1676,10 @@ def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Is either a ChatRole type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRole or - ~azure.messaging.webpubsubchatservice.types.ChatRole or IO[bytes] + :param role: The resource instance. Is either a ChatRoleCreateOrReplace type or a IO[bytes] + type. Required. + :type role: ~azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoleCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1463,10 +1711,10 @@ def create_or_replace_role( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(role, (IOBase, bytes)): + _content = role else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(role, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_role_request( role_name=role_name, @@ -1585,34 +1833,133 @@ def delete_role( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore - @overload - def create_or_replace_room( - self, - room_id: str, - resource: _models.ChatRoom, - *, - content_type: str = "application/json", - etag: Optional[str] = None, - match_condition: Optional[MatchConditions] = None, - **kwargs: Any - ) -> _models.ChatRoom: - """Create or replace a room. + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_rooms(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> ItemPaged["_models.ChatRoom"]: + """List rooms with pagination. - Create or replace a room with a client-specified ID. + List rooms with pagination. - :param room_id: Room identifier. Required. - :type room_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoom - :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". - :paramtype content_type: str - :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is - None. - :paramtype etag: str - :keyword match_condition: The match condition to use upon the etag. Default value is None. - :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatRoom. The ChatRoom is compatible with MutableMapping + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatRoom + :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRoom] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatRoom]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_rooms_request( + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatRoom], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @overload + def create_or_replace_room( + self, + room_id: str, + room: _models.ChatRoomCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatRoom: + """Create or replace a room. + + Create or replace a room with a client-specified ID. + + :param room_id: Room identifier. Required. + :type room_id: str + :param room: The resource instance. Required. + :type room: ~azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatRoom. The ChatRoom is compatible with MutableMapping :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoom :raises ~azure.core.exceptions.HttpResponseError: """ @@ -1621,7 +1968,7 @@ def create_or_replace_room( def create_or_replace_room( self, room_id: str, - resource: _types.ChatRoom, + room: _types.ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1634,8 +1981,8 @@ def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRoom + :param room: The resource instance. Required. + :type room: ~azure.messaging.webpubsubchatservice.types.ChatRoomCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1653,7 +2000,7 @@ def create_or_replace_room( def create_or_replace_room( self, room_id: str, - resource: IO[bytes], + room: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1666,8 +2013,8 @@ def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param room: The resource instance. Required. + :type room: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -1685,7 +2032,7 @@ def create_or_replace_room( def create_or_replace_room( self, room_id: str, - resource: Union[_models.ChatRoom, _types.ChatRoom, IO[bytes]], + room: Union[_models.ChatRoomCreateOrReplace, _types.ChatRoomCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -1697,10 +2044,10 @@ def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Is either a ChatRoom type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoom or - ~azure.messaging.webpubsubchatservice.types.ChatRoom or IO[bytes] + :param room: The resource instance. Is either a ChatRoomCreateOrReplace type or a IO[bytes] + type. Required. + :type room: ~azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoomCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1728,17 +2075,782 @@ def create_or_replace_room( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ChatRoom] = kwargs.pop("cls", None) + cls: ClsType[_models.ChatRoom] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(room, (IOBase, bytes)): + _content = room + else: + _content = json.dumps(room, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_web_pub_sub_chat_service_create_or_replace_room_request( + room_id=room_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatRoom, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_room(self, room_id: str, **kwargs: Any) -> _models.ChatRoom: + """Get room information. + + Get room information. + + :param room_id: Room identifier. Required. + :type room_id: str + :return: ChatRoom. The ChatRoom is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoom + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.ChatRoom] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_get_room_request( + room_id=room_id, + hub=self._config.hub, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatRoom, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete_room( # pylint: disable=inconsistent-return-statements + self, + room_id: str, + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> None: + """Delete a room. + + Delete a room. + + :param room_id: Room identifier. Required. + :type room_id: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_delete_room_request( + room_id=room_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "room_id", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_topics( + self, room_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any + ) -> ItemPaged["_models.ChatTopic"]: + """List topics in a room with pagination. + + List topics in a room with pagination. + + :param room_id: Room identifier. Required. + :type room_id: str + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatTopic + :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatTopic] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatTopic]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_topics_request( + room_id=room_id, + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatTopic], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: _models.ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Create or replace a topic. + + Create or replace a topic with a client-specified ID. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: _types.ChatTopicCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Create or replace a topic. + + Create or replace a topic with a client-specified ID. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.types.ChatTopicCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Create or replace a topic. + + Create or replace a topic with a client-specified ID. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": [ + "api_version", + "hub", + "room_id", + "topic_id", + "content_type", + "accept", + "etag", + "match_condition", + ] + }, + api_versions_list=["2026-08-01-preview"], + ) + def create_or_replace_topic( + self, + room_id: str, + topic_id: str, + topic: Union[_models.ChatTopicCreateOrReplace, _types.ChatTopicCreateOrReplace, IO[bytes]], + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Create or replace a topic. + + Create or replace a topic with a client-specified ID. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Is either a ChatTopicCreateOrReplace type or a IO[bytes] + type. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatTopicCreateOrReplace or IO[bytes] + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(topic, (IOBase, bytes)): + _content = topic + else: + _content = json.dumps(topic, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_web_pub_sub_chat_service_create_or_replace_topic_request( + room_id=room_id, + topic_id=topic_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatTopic, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "room_id", "topic_id", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def get_topic(self, room_id: str, topic_id: str, **kwargs: Any) -> _models.ChatTopic: + """Get topic information. + + Get topic information. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_get_topic_request( + room_id=room_id, + topic_id=topic_id, + hub=self._config.hub, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatTopic, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: _models.ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicUpdate + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: _types.ChatTopicUpdate, + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.types.ChatTopicUpdate + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": [ + "api_version", + "hub", + "room_id", + "topic_id", + "content_type", + "accept", + "etag", + "match_condition", + ] + }, + api_versions_list=["2026-08-01-preview"], + ) + def update_topic( + self, + room_id: str, + topic_id: str, + topic: Union[_models.ChatTopicUpdate, _types.ChatTopicUpdate, IO[bytes]], + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Is either a ChatTopicUpdate type or a IO[bytes] type. + Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicUpdate or + ~azure.messaging.webpubsubchatservice.types.ChatTopicUpdate or IO[bytes] + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) - content_type = content_type or "application/json" + content_type = content_type or "application/merge-patch+json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(topic, (IOBase, bytes)): + _content = topic else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(topic, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_web_pub_sub_chat_service_create_or_replace_room_request( + _request = build_web_pub_sub_chat_service_update_topic_request( room_id=room_id, + topic_id=topic_id, hub=self._config.hub, etag=etag, match_condition=match_condition, @@ -1761,7 +2873,7 @@ def create_or_replace_room( response = pipeline_response.http_response - if response.status_code not in [200, 201]: + if response.status_code not in [200]: if _stream: try: response.read() # Load the body in memory and close the socket @@ -1776,7 +2888,7 @@ def create_or_replace_room( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ChatRoom, response.json()) + deserialized = _deserialize(_models.ChatTopic, response.json()) if cls: return cls(pipeline_response, deserialized, response_headers) # type: ignore @@ -1784,84 +2896,30 @@ def create_or_replace_room( return deserialized # type: ignore @distributed_trace - def get_room(self, room_id: str, **kwargs: Any) -> _models.ChatRoom: - """Get room information. - - Get room information. - - :param room_id: Room identifier. Required. - :type room_id: str - :return: ChatRoom. The ChatRoom is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoom - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.ChatRoom] = kwargs.pop("cls", None) - - _request = build_web_pub_sub_chat_service_get_room_request( - room_id=room_id, - hub=self._config.hub, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _decompress = kwargs.pop("decompress", True) - _stream = kwargs.pop("stream", False) - pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [200]: - if _stream: - try: - response.read() # Load the body in memory and close the socket - except (StreamConsumedError, StreamClosedError): - pass - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if _stream: - deserialized = response.iter_bytes() if _decompress else response.iter_raw() - else: - deserialized = _deserialize(_models.ChatRoom, response.json()) - - if cls: - return cls(pipeline_response, deserialized, {}) # type: ignore - - return deserialized # type: ignore - - @distributed_trace - def delete_room( # pylint: disable=inconsistent-return-statements + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": ["api_version", "hub", "room_id", "topic_id", "etag", "match_condition"] + }, + api_versions_list=["2026-08-01-preview"], + ) + def delete_topic( # pylint: disable=inconsistent-return-statements self, room_id: str, + topic_id: str, *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any ) -> None: - """Delete a room. + """Soft delete a topic. - Delete a room. + Soft delete a topic. :param room_id: Room identifier. Required. :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1890,8 +2948,9 @@ def delete_room( # pylint: disable=inconsistent-return-statements cls: ClsType[None] = kwargs.pop("cls", None) - _request = build_web_pub_sub_chat_service_delete_room_request( + _request = build_web_pub_sub_chat_service_delete_topic_request( room_id=room_id, + topic_id=topic_id, hub=self._config.hub, etag=etag, match_condition=match_condition, @@ -1920,12 +2979,7 @@ def delete_room( # pylint: disable=inconsistent-return-statements @distributed_trace def list_room_members( - self, - room_id: str, - *, - max_page_size: Optional[int] = None, - continuation_token_parameter: Optional[str] = None, - **kwargs: Any + self, room_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any ) -> ItemPaged["_models.ChatRoomMember"]: """Get room members. @@ -1935,9 +2989,6 @@ def list_room_members( :type room_id: str :keyword max_page_size: The maximum number of result items per page. Default value is None. :paramtype max_page_size: int - :keyword continuation_token_parameter: Continuation token for pagination. Default value is - None. - :paramtype continuation_token_parameter: str :return: An iterator like instance of ChatRoomMember :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRoomMember] @@ -1963,7 +3014,6 @@ def prepare_request(next_link=None): room_id=room_id, hub=self._config.hub, max_page_size=max_page_size, - continuation_token_parameter=continuation_token_parameter, api_version=self._config.api_version, headers=_headers, params=_params, @@ -2032,7 +3082,7 @@ def create_or_replace_room_member( self, room_id: str, user_id: str, - resource: _models.ChatRoomMember, + member: _models.ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2047,8 +3097,8 @@ def create_or_replace_room_member( :type room_id: str :param user_id: User ID of the member. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :param member: The resource instance. Required. + :type member: ~azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -2067,7 +3117,7 @@ def create_or_replace_room_member( self, room_id: str, user_id: str, - resource: _types.ChatRoomMember, + member: _types.ChatRoomMemberCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2082,8 +3132,8 @@ def create_or_replace_room_member( :type room_id: str :param user_id: User ID of the member. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRoomMember + :param member: The resource instance. Required. + :type member: ~azure.messaging.webpubsubchatservice.types.ChatRoomMemberCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -2102,7 +3152,7 @@ def create_or_replace_room_member( self, room_id: str, user_id: str, - resource: IO[bytes], + member: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2117,8 +3167,8 @@ def create_or_replace_room_member( :type room_id: str :param user_id: User ID of the member. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param member: The resource instance. Required. + :type member: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -2137,7 +3187,7 @@ def create_or_replace_room_member( self, room_id: str, user_id: str, - resource: Union[_models.ChatRoomMember, _types.ChatRoomMember, IO[bytes]], + member: Union[_models.ChatRoomMemberCreateOrReplace, _types.ChatRoomMemberCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -2151,10 +3201,10 @@ def create_or_replace_room_member( :type room_id: str :param user_id: User ID of the member. Required. :type user_id: str - :param resource: The resource instance. Is either a ChatRoomMember type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember or - ~azure.messaging.webpubsubchatservice.types.ChatRoomMember or IO[bytes] + :param member: The resource instance. Is either a ChatRoomMemberCreateOrReplace type or a + IO[bytes] type. Required. + :type member: ~azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoomMemberCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -2186,10 +3236,10 @@ def create_or_replace_room_member( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(member, (IOBase, bytes)): + _content = member else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(member, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_room_member_request( room_id=room_id, @@ -2313,6 +3363,105 @@ def delete_room_member( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_users(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> ItemPaged["_models.ChatUser"]: + """List all users with pagination. + + List all users with pagination. + + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatUser + :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatUser] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatUser]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_users_request( + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatUser], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + @distributed_trace def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: """Get a user's profile. @@ -2382,7 +3531,7 @@ def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: def create_or_replace_user( self, user_id: str, - resource: _models.ChatUser, + user: _models.ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2396,8 +3545,8 @@ def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatUser + :param user: The resource instance. Required. + :type user: ~azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -2415,7 +3564,7 @@ def create_or_replace_user( def create_or_replace_user( self, user_id: str, - resource: _types.ChatUser, + user: _types.ChatUserCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2429,8 +3578,8 @@ def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatUser + :param user: The resource instance. Required. + :type user: ~azure.messaging.webpubsubchatservice.types.ChatUserCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -2448,7 +3597,7 @@ def create_or_replace_user( def create_or_replace_user( self, user_id: str, - resource: IO[bytes], + user: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -2462,8 +3611,8 @@ def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param user: The resource instance. Required. + :type user: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -2481,7 +3630,7 @@ def create_or_replace_user( def create_or_replace_user( self, user_id: str, - resource: Union[_models.ChatUser, _types.ChatUser, IO[bytes]], + user: Union[_models.ChatUserCreateOrReplace, _types.ChatUserCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -2494,10 +3643,10 @@ def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Is either a ChatUser type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatUser or - ~azure.messaging.webpubsubchatservice.types.ChatUser or IO[bytes] + :param user: The resource instance. Is either a ChatUserCreateOrReplace type or a IO[bytes] + type. Required. + :type user: ~azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatUserCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -2529,10 +3678,10 @@ def create_or_replace_user( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(user, (IOBase, bytes)): + _content = user else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(user, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_user_request( user_id=user_id, @@ -2651,6 +3800,110 @@ def delete_user( # pylint: disable=inconsistent-return-statements if cls: return cls(pipeline_response, None, {}) # type: ignore + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "user_id", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_rooms_for_user( + self, user_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any + ) -> ItemPaged["_models.ChatUserRoom"]: + """List rooms for a user with pagination. + + List rooms for a user with pagination. + + :param user_id: User identifier. Required. + :type user_id: str + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatUserRoom + :rtype: ~azure.core.paging.ItemPaged[~azure.messaging.webpubsubchatservice.models.ChatUserRoom] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatUserRoom]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_rooms_for_user_request( + user_id=user_id, + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatUserRoom], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, iter(list_of_elem) + + def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + @distributed_trace def _generate_client_token( self, diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_validation.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_validation.py new file mode 100644 index 000000000000..f5af3a4eb8a2 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_validation.py @@ -0,0 +1,66 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +import functools + + +def api_version_validation(**kwargs): + params_added_on = kwargs.pop("params_added_on", {}) + method_added_on = kwargs.pop("method_added_on", "") + api_versions_list = kwargs.pop("api_versions_list", []) + + def _index_with_default(value: str, default: int = -1) -> int: + """Get the index of value in lst, or return default if not found. + + :param value: The value to search for in the api_versions_list. + :type value: str + :param default: The default value to return if the value is not found. + :type default: int + :return: The index of the value in the list, or the default value if not found. + :rtype: int + """ + try: + return api_versions_list.index(value) + except ValueError: + return default + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + # this assumes the client has an _api_version attribute + client = args[0] + client_api_version = client._config.api_version # pylint: disable=protected-access + except AttributeError: + return func(*args, **kwargs) + + if _index_with_default(method_added_on) > _index_with_default(client_api_version): + raise ValueError( + f"'{func.__name__}' is not available in API version " + f"{client_api_version}. Pass service API version {method_added_on} or newer to your client." + ) + + unsupported = { + parameter: api_version + for api_version, parameters in params_added_on.items() + for parameter in parameters + if parameter in kwargs and _index_with_default(api_version) > _index_with_default(client_api_version) + } + if unsupported: + raise ValueError( + "".join( + [ + f"'{param}' is not available in API version {client_api_version}. " + f"Use service API version {version} or newer.\n" + for param, version in unsupported.items() + ] + ) + ) + return func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_version.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_version.py index be71c81bd282..bbcd28b4aa67 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_version.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/_version.py @@ -6,4 +6,4 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -VERSION = "1.0.0b1" +VERSION = "1.0.0b2" diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_client.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_client.py index 6abc2e988347..862a1a451bfe 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_client.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_client.py @@ -40,7 +40,7 @@ class WebPubSubChatServiceClient( alpha-numeric characters or underscore. Required. :type hub: str :keyword api_version: The API version to use for this operation. Known values are - "2026-02-01-preview" and None. Default value is None. If not set, the operation's default API + "2026-08-01-preview" and None. Default value is None. If not set, the operation's default API version will be used. Note that overriding this default value may result in unsupported behavior. :paramtype api_version: str diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_configuration.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_configuration.py index 564c0963ed46..57ae09abfe43 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_configuration.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_configuration.py @@ -31,14 +31,14 @@ class WebPubSubChatServiceClientConfiguration: # pylint: disable=too-many-insta alpha-numeric characters or underscore. Required. :type hub: str :keyword api_version: The API version to use for this operation. Known values are - "2026-02-01-preview" and None. Default value is None. If not set, the operation's default API + "2026-08-01-preview" and None. Default value is None. If not set, the operation's default API version will be used. Note that overriding this default value may result in unsupported behavior. :paramtype api_version: str """ def __init__(self, endpoint: str, credential: "AsyncTokenCredential", hub: str, **kwargs: Any) -> None: - api_version: str = kwargs.pop("api_version", "2026-02-01-preview") + api_version: str = kwargs.pop("api_version", "2026-08-01-preview") if endpoint is None: raise ValueError("Parameter 'endpoint' must not be None.") diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_operations/_operations.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_operations/_operations.py index 17d17a8452df..268386a7ebfa 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_operations/_operations.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/aio/_operations/_operations.py @@ -36,31 +36,40 @@ build_web_pub_sub_chat_service_create_or_replace_role_request, build_web_pub_sub_chat_service_create_or_replace_room_member_request, build_web_pub_sub_chat_service_create_or_replace_room_request, + build_web_pub_sub_chat_service_create_or_replace_topic_request, build_web_pub_sub_chat_service_create_or_replace_user_request, build_web_pub_sub_chat_service_delete_message_request, build_web_pub_sub_chat_service_delete_role_request, build_web_pub_sub_chat_service_delete_room_member_request, build_web_pub_sub_chat_service_delete_room_request, + build_web_pub_sub_chat_service_delete_topic_request, build_web_pub_sub_chat_service_delete_user_request, build_web_pub_sub_chat_service_generate_client_token_request, build_web_pub_sub_chat_service_get_conversation_request, build_web_pub_sub_chat_service_get_role_request, build_web_pub_sub_chat_service_get_room_request, + build_web_pub_sub_chat_service_get_topic_request, build_web_pub_sub_chat_service_get_user_request, build_web_pub_sub_chat_service_list_messages_request, build_web_pub_sub_chat_service_list_roles_request, build_web_pub_sub_chat_service_list_room_members_request, + build_web_pub_sub_chat_service_list_rooms_for_user_request, + build_web_pub_sub_chat_service_list_rooms_request, + build_web_pub_sub_chat_service_list_topics_request, + build_web_pub_sub_chat_service_list_users_request, build_web_pub_sub_chat_service_update_message_request, + build_web_pub_sub_chat_service_update_topic_request, ) from ..._utils.model_base import SdkJSONEncoder, _deserialize from ..._utils.utils import ClientMixinABC +from ..._validation import api_version_validation from .._configuration import WebPubSubChatServiceClientConfiguration T = TypeVar("T") ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, dict[str, Any]], Any]] -class _WebPubSubChatServiceClientOperationsMixin( +class _WebPubSubChatServiceClientOperationsMixin( # pylint: disable=too-many-public-methods ClientMixinABC[AsyncPipelineClient[HttpRequest, AsyncHttpResponse], WebPubSubChatServiceClientConfiguration] ): @@ -140,7 +149,8 @@ def list_messages( ) -> AsyncItemPaged["_models.ChatMessage"]: """Query messages in a conversation from latest to earliest. - Query messages in a conversation from latest to earliest. + Message IDs are used as pagination cursors. Follow the response nextLink, which contains the + message ID cursor, to retrieve the next page. :param conversation_id: Conversation identifier. Required. :type conversation_id: str @@ -322,7 +332,7 @@ async def update_message( self, conversation_id: str, message_id: str, - resource: _models.ChatMessage, + message: _models.ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -337,8 +347,8 @@ async def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatMessage + :param message: The resource instance. Required. + :type message: ~azure.messaging.webpubsubchatservice.models.ChatMessageUpdate :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -357,7 +367,7 @@ async def update_message( self, conversation_id: str, message_id: str, - resource: _types.ChatMessage, + message: _types.ChatMessageUpdate, *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -372,8 +382,8 @@ async def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatMessage + :param message: The resource instance. Required. + :type message: ~azure.messaging.webpubsubchatservice.types.ChatMessageUpdate :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -392,7 +402,7 @@ async def update_message( self, conversation_id: str, message_id: str, - resource: IO[bytes], + message: IO[bytes], *, content_type: str = "application/merge-patch+json", etag: Optional[str] = None, @@ -407,8 +417,8 @@ async def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param message: The resource instance. Required. + :type message: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/merge-patch+json". :paramtype content_type: str @@ -427,7 +437,7 @@ async def update_message( self, conversation_id: str, message_id: str, - resource: Union[_models.ChatMessage, _types.ChatMessage, IO[bytes]], + message: Union[_models.ChatMessageUpdate, _types.ChatMessageUpdate, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -441,10 +451,10 @@ async def update_message( :type conversation_id: str :param message_id: Message identifier. Required. :type message_id: str - :param resource: The resource instance. Is either a ChatMessage type or a IO[bytes] type. + :param message: The resource instance. Is either a ChatMessageUpdate type or a IO[bytes] type. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatMessage or - ~azure.messaging.webpubsubchatservice.types.ChatMessage or IO[bytes] + :type message: ~azure.messaging.webpubsubchatservice.models.ChatMessageUpdate or + ~azure.messaging.webpubsubchatservice.types.ChatMessageUpdate or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -476,10 +486,10 @@ async def update_message( content_type = content_type or "application/merge-patch+json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(message, (IOBase, bytes)): + _content = message else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(message, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_update_message_request( conversation_id=conversation_id, @@ -529,18 +539,13 @@ async def update_message( return deserialized # type: ignore @distributed_trace - def list_roles( - self, *, max_page_size: Optional[int] = None, continuation_token_parameter: Optional[str] = None, **kwargs: Any - ) -> AsyncItemPaged["_models.ChatRole"]: + def list_roles(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> AsyncItemPaged["_models.ChatRole"]: """Query roles in a hub. Query roles in a hub. :keyword max_page_size: The maximum number of result items per page. Default value is None. :paramtype max_page_size: int - :keyword continuation_token_parameter: Continuation token for pagination. Default value is - None. - :paramtype continuation_token_parameter: str :return: An iterator like instance of ChatRole :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRole] @@ -565,7 +570,6 @@ def prepare_request(next_link=None): _request = build_web_pub_sub_chat_service_list_roles_request( hub=self._config.hub, max_page_size=max_page_size, - continuation_token_parameter=continuation_token_parameter, api_version=self._config.api_version, headers=_headers, params=_params, @@ -697,7 +701,7 @@ async def get_role(self, role_name: str, **kwargs: Any) -> _models.ChatRole: async def create_or_replace_role( self, role_name: str, - resource: _models.ChatRole, + role: _models.ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -710,8 +714,8 @@ async def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRole + :param role: The resource instance. Required. + :type role: ~azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -729,7 +733,7 @@ async def create_or_replace_role( async def create_or_replace_role( self, role_name: str, - resource: _types.ChatRole, + role: _types.ChatRoleCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -742,8 +746,8 @@ async def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRole + :param role: The resource instance. Required. + :type role: ~azure.messaging.webpubsubchatservice.types.ChatRoleCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -761,7 +765,7 @@ async def create_or_replace_role( async def create_or_replace_role( self, role_name: str, - resource: IO[bytes], + role: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -774,8 +778,8 @@ async def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param role: The resource instance. Required. + :type role: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -793,7 +797,7 @@ async def create_or_replace_role( async def create_or_replace_role( self, role_name: str, - resource: Union[_models.ChatRole, _types.ChatRole, IO[bytes]], + role: Union[_models.ChatRoleCreateOrReplace, _types.ChatRoleCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -805,10 +809,10 @@ async def create_or_replace_role( :param role_name: Role name. Must start with 'user.' or 'room.' prefix. Required. :type role_name: str - :param resource: The resource instance. Is either a ChatRole type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRole or - ~azure.messaging.webpubsubchatservice.types.ChatRole or IO[bytes] + :param role: The resource instance. Is either a ChatRoleCreateOrReplace type or a IO[bytes] + type. Required. + :type role: ~azure.messaging.webpubsubchatservice.models.ChatRoleCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoleCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -840,10 +844,10 @@ async def create_or_replace_role( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(role, (IOBase, bytes)): + _content = role else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(role, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_role_request( role_name=role_name, @@ -962,11 +966,111 @@ async def delete_role( if cls: return cls(pipeline_response, None, {}) # type: ignore + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_rooms(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> AsyncItemPaged["_models.ChatRoom"]: + """List rooms with pagination. + + List rooms with pagination. + + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatRoom + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRoom] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatRoom]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_rooms_request( + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatRoom], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + @overload async def create_or_replace_room( self, room_id: str, - resource: _models.ChatRoom, + room: _models.ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -979,8 +1083,8 @@ async def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoom + :param room: The resource instance. Required. + :type room: ~azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -998,7 +1102,7 @@ async def create_or_replace_room( async def create_or_replace_room( self, room_id: str, - resource: _types.ChatRoom, + room: _types.ChatRoomCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1011,8 +1115,8 @@ async def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRoom + :param room: The resource instance. Required. + :type room: ~azure.messaging.webpubsubchatservice.types.ChatRoomCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1030,7 +1134,7 @@ async def create_or_replace_room( async def create_or_replace_room( self, room_id: str, - resource: IO[bytes], + room: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1043,8 +1147,8 @@ async def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param room: The resource instance. Required. + :type room: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -1062,7 +1166,7 @@ async def create_or_replace_room( async def create_or_replace_room( self, room_id: str, - resource: Union[_models.ChatRoom, _types.ChatRoom, IO[bytes]], + room: Union[_models.ChatRoomCreateOrReplace, _types.ChatRoomCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -1074,10 +1178,10 @@ async def create_or_replace_room( :param room_id: Room identifier. Required. :type room_id: str - :param resource: The resource instance. Is either a ChatRoom type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoom or - ~azure.messaging.webpubsubchatservice.types.ChatRoom or IO[bytes] + :param room: The resource instance. Is either a ChatRoomCreateOrReplace type or a IO[bytes] + type. Required. + :type room: ~azure.messaging.webpubsubchatservice.models.ChatRoomCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoomCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1109,10 +1213,10 @@ async def create_or_replace_room( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(room, (IOBase, bytes)): + _content = room else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(room, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_room_request( room_id=room_id, @@ -1296,34 +1400,31 @@ async def delete_room( return cls(pipeline_response, None, {}) # type: ignore @distributed_trace - def list_room_members( - self, - room_id: str, - *, - max_page_size: Optional[int] = None, - continuation_token_parameter: Optional[str] = None, - **kwargs: Any - ) -> AsyncItemPaged["_models.ChatRoomMember"]: - """Get room members. - - Get room members. + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "room_id", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_topics( + self, room_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any + ) -> AsyncItemPaged["_models.ChatTopic"]: + """List topics in a room with pagination. + + List topics in a room with pagination. :param room_id: Room identifier. Required. :type room_id: str :keyword max_page_size: The maximum number of result items per page. Default value is None. :paramtype max_page_size: int - :keyword continuation_token_parameter: Continuation token for pagination. Default value is - None. - :paramtype continuation_token_parameter: str - :return: An iterator like instance of ChatRoomMember + :return: An iterator like instance of ChatTopic :rtype: - ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRoomMember] + ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatTopic] :raises ~azure.core.exceptions.HttpResponseError: """ _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[list[_models.ChatRoomMember]] = kwargs.pop("cls", None) + cls: ClsType[list[_models.ChatTopic]] = kwargs.pop("cls", None) error_map: MutableMapping = { 401: ClientAuthenticationError, @@ -1336,11 +1437,10 @@ def list_room_members( def prepare_request(next_link=None): if not next_link: - _request = build_web_pub_sub_chat_service_list_room_members_request( + _request = build_web_pub_sub_chat_service_list_topics_request( room_id=room_id, hub=self._config.hub, max_page_size=max_page_size, - continuation_token_parameter=continuation_token_parameter, api_version=self._config.api_version, headers=_headers, params=_params, @@ -1380,7 +1480,7 @@ def prepare_request(next_link=None): async def extract_data(pipeline_response): deserialized = pipeline_response.http_response.json() list_of_elem = _deserialize( - list[_models.ChatRoomMember], + list[_models.ChatTopic], deserialized.get("value", []), ) if cls: @@ -1405,27 +1505,27 @@ async def get_next(next_link=None): return AsyncItemPaged(get_next, extract_data) @overload - async def create_or_replace_room_member( + async def create_or_replace_topic( self, room_id: str, - user_id: str, - resource: _models.ChatRoomMember, + topic_id: str, + topic: _models.ChatTopicCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatRoomMember: - """Create or replace a room member. + ) -> _models.ChatTopic: + """Create or replace a topic. - Create or replace a room member. + Create or replace a topic with a client-specified ID. :param room_id: Room identifier. Required. :type room_id: str - :param user_id: User ID of the member. Required. - :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1434,33 +1534,33 @@ async def create_or_replace_room_member( :paramtype etag: str :keyword match_condition: The match condition to use upon the etag. Default value is None. :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def create_or_replace_room_member( + async def create_or_replace_topic( self, room_id: str, - user_id: str, - resource: _types.ChatRoomMember, + topic_id: str, + topic: _types.ChatTopicCreateOrReplace, *, content_type: str = "application/json", etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatRoomMember: - """Create or replace a room member. + ) -> _models.ChatTopic: + """Create or replace a topic. - Create or replace a room member. + Create or replace a topic with a client-specified ID. :param room_id: Room identifier. Required. :type room_id: str - :param user_id: User ID of the member. Required. - :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatRoomMember + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.types.ChatTopicCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1469,33 +1569,33 @@ async def create_or_replace_room_member( :paramtype etag: str :keyword match_condition: The match condition to use upon the etag. Default value is None. :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def create_or_replace_room_member( + async def create_or_replace_topic( self, room_id: str, - user_id: str, - resource: IO[bytes], + topic_id: str, + topic: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatRoomMember: - """Create or replace a room member. + ) -> _models.ChatTopic: + """Create or replace a topic. - Create or replace a room member. + Create or replace a topic with a client-specified ID. :param room_id: Room identifier. Required. :type room_id: str - :param user_id: User ID of the member. Required. - :type user_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -1504,41 +1604,57 @@ async def create_or_replace_room_member( :paramtype etag: str :keyword match_condition: The match condition to use upon the etag. Default value is None. :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ @distributed_trace_async - async def create_or_replace_room_member( + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": [ + "api_version", + "hub", + "room_id", + "topic_id", + "content_type", + "accept", + "etag", + "match_condition", + ] + }, + api_versions_list=["2026-08-01-preview"], + ) + async def create_or_replace_topic( self, room_id: str, - user_id: str, - resource: Union[_models.ChatRoomMember, _types.ChatRoomMember, IO[bytes]], + topic_id: str, + topic: Union[_models.ChatTopicCreateOrReplace, _types.ChatTopicCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatRoomMember: - """Create or replace a room member. + ) -> _models.ChatTopic: + """Create or replace a topic. - Create or replace a room member. + Create or replace a topic with a client-specified ID. :param room_id: Room identifier. Required. :type room_id: str - :param user_id: User ID of the member. Required. - :type user_id: str - :param resource: The resource instance. Is either a ChatRoomMember type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember or - ~azure.messaging.webpubsubchatservice.types.ChatRoomMember or IO[bytes] + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Is either a ChatTopicCreateOrReplace type or a IO[bytes] + type. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatTopicCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str :keyword match_condition: The match condition to use upon the etag. Default value is None. :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -1559,18 +1675,18 @@ async def create_or_replace_room_member( _params = kwargs.pop("params", {}) or {} content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - cls: ClsType[_models.ChatRoomMember] = kwargs.pop("cls", None) + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(topic, (IOBase, bytes)): + _content = topic else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(topic, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore - _request = build_web_pub_sub_chat_service_create_or_replace_room_member_request( + _request = build_web_pub_sub_chat_service_create_or_replace_topic_request( room_id=room_id, - user_id=user_id, + topic_id=topic_id, hub=self._config.hub, etag=etag, match_condition=match_condition, @@ -1608,7 +1724,7 @@ async def create_or_replace_room_member( if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ChatRoomMember, response.json()) + deserialized = _deserialize(_models.ChatTopic, response.json()) if cls: return cls(pipeline_response, deserialized, response_headers) # type: ignore @@ -1616,30 +1732,22 @@ async def create_or_replace_room_member( return deserialized # type: ignore @distributed_trace_async - async def delete_room_member( - self, - room_id: str, - user_id: str, - *, - etag: Optional[str] = None, - match_condition: Optional[MatchConditions] = None, - **kwargs: Any - ) -> None: - """Delete a room member. + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "room_id", "topic_id", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + async def get_topic(self, room_id: str, topic_id: str, **kwargs: Any) -> _models.ChatTopic: + """Get topic information. - Delete a room member. + Get topic information. :param room_id: Room identifier. Required. :type room_id: str - :param user_id: User ID of the member. Required. - :type user_id: str - :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is - None. - :paramtype etag: str - :keyword match_condition: The match condition to use upon the etag. Default value is None. - :paramtype match_condition: ~azure.core.MatchConditions - :return: None - :rtype: None + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ error_map: MutableMapping = { @@ -1648,76 +1756,16 @@ async def delete_room_member( 409: ResourceExistsError, 304: ResourceNotModifiedError, } - if match_condition == MatchConditions.IfNotModified: - error_map[412] = ResourceModifiedError - elif match_condition == MatchConditions.IfPresent: - error_map[412] = ResourceNotFoundError - elif match_condition == MatchConditions.IfMissing: - error_map[412] = ResourceExistsError error_map.update(kwargs.pop("error_map", {}) or {}) _headers = kwargs.pop("headers", {}) or {} _params = kwargs.pop("params", {}) or {} - cls: ClsType[None] = kwargs.pop("cls", None) + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) - _request = build_web_pub_sub_chat_service_delete_room_member_request( + _request = build_web_pub_sub_chat_service_get_topic_request( room_id=room_id, - user_id=user_id, - hub=self._config.hub, - etag=etag, - match_condition=match_condition, - api_version=self._config.api_version, - headers=_headers, - params=_params, - ) - path_format_arguments = { - "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), - } - _request.url = self._client.format_url(_request.url, **path_format_arguments) - - _stream = False - pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access - _request, stream=_stream, **kwargs - ) - - response = pipeline_response.http_response - - if response.status_code not in [204]: - map_error(status_code=response.status_code, response=response, error_map=error_map) - raise HttpResponseError(response=response) - - if cls: - return cls(pipeline_response, None, {}) # type: ignore - - @distributed_trace_async - async def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: - """Get a user's profile. - - Get a user's profile. The response is a polymorphic ``ChatUser`` (e.g. ``HumanChatUser``) - selected by the ``kind`` discriminator. - - :param user_id: User identifier. Required. - :type user_id: str - :return: ChatUser. The ChatUser is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatUser - :raises ~azure.core.exceptions.HttpResponseError: - """ - error_map: MutableMapping = { - 401: ClientAuthenticationError, - 404: ResourceNotFoundError, - 409: ResourceExistsError, - 304: ResourceNotModifiedError, - } - error_map.update(kwargs.pop("error_map", {}) or {}) - - _headers = kwargs.pop("headers", {}) or {} - _params = kwargs.pop("params", {}) or {} - - cls: ClsType[_models.ChatUser] = kwargs.pop("cls", None) - - _request = build_web_pub_sub_chat_service_get_user_request( - user_id=user_id, + topic_id=topic_id, hub=self._config.hub, api_version=self._config.api_version, headers=_headers, @@ -1748,7 +1796,7 @@ async def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: if _stream: deserialized = response.iter_bytes() if _decompress else response.iter_raw() else: - deserialized = _deserialize(_models.ChatUser, response.json()) + deserialized = _deserialize(_models.ChatTopic, response.json()) if cls: return cls(pipeline_response, deserialized, {}) # type: ignore @@ -1756,58 +1804,918 @@ async def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: return deserialized # type: ignore @overload - async def create_or_replace_user( + async def update_topic( self, - user_id: str, - resource: _models.ChatUser, + room_id: str, + topic_id: str, + topic: _models.ChatTopicUpdate, *, - content_type: str = "application/json", + content_type: str = "application/merge-patch+json", etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatUser: - """Create or replace a user. + ) -> _models.ChatTopic: + """Update a topic. - Create or replace a user. The request body is a polymorphic ``ChatUser`` (e.g. - ``HumanChatUser``) selected by the ``kind`` discriminator. + Update a topic. - :param user_id: User identifier. Required. - :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatUser + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicUpdate :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. - Default value is "application/json". + Default value is "application/merge-patch+json". :paramtype content_type: str :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str :keyword match_condition: The match condition to use upon the etag. Default value is None. :paramtype match_condition: ~azure.core.MatchConditions - :return: ChatUser. The ChatUser is compatible with MutableMapping - :rtype: ~azure.messaging.webpubsubchatservice.models.ChatUser + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic :raises ~azure.core.exceptions.HttpResponseError: """ @overload - async def create_or_replace_user( + async def update_topic( self, - user_id: str, - resource: _types.ChatUser, + room_id: str, + topic_id: str, + topic: _types.ChatTopicUpdate, *, - content_type: str = "application/json", + content_type: str = "application/merge-patch+json", etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, **kwargs: Any - ) -> _models.ChatUser: - """Create or replace a user. + ) -> _models.ChatTopic: + """Update a topic. - Create or replace a user. The request body is a polymorphic ``ChatUser`` (e.g. + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: ~azure.messaging.webpubsubchatservice.types.ChatTopicUpdate + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update_topic( + self, + room_id: str, + topic_id: str, + topic: IO[bytes], + *, + content_type: str = "application/merge-patch+json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Required. + :type topic: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/merge-patch+json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": [ + "api_version", + "hub", + "room_id", + "topic_id", + "content_type", + "accept", + "etag", + "match_condition", + ] + }, + api_versions_list=["2026-08-01-preview"], + ) + async def update_topic( + self, + room_id: str, + topic_id: str, + topic: Union[_models.ChatTopicUpdate, _types.ChatTopicUpdate, IO[bytes]], + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatTopic: + """Update a topic. + + Update a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :param topic: The resource instance. Is either a ChatTopicUpdate type or a IO[bytes] type. + Required. + :type topic: ~azure.messaging.webpubsubchatservice.models.ChatTopicUpdate or + ~azure.messaging.webpubsubchatservice.types.ChatTopicUpdate or IO[bytes] + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatTopic. The ChatTopic is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatTopic + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ChatTopic] = kwargs.pop("cls", None) + + content_type = content_type or "application/merge-patch+json" + _content = None + if isinstance(topic, (IOBase, bytes)): + _content = topic + else: + _content = json.dumps(topic, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_web_pub_sub_chat_service_update_topic_request( + room_id=room_id, + topic_id=topic_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatTopic, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={ + "2026-08-01-preview": ["api_version", "hub", "room_id", "topic_id", "etag", "match_condition"] + }, + api_versions_list=["2026-08-01-preview"], + ) + async def delete_topic( + self, + room_id: str, + topic_id: str, + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> None: + """Soft delete a topic. + + Soft delete a topic. + + :param room_id: Room identifier. Required. + :type room_id: str + :param topic_id: Topic identifier unique within the room. Required. + :type topic_id: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_delete_topic_request( + room_id=room_id, + topic_id=topic_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace + def list_room_members( + self, room_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any + ) -> AsyncItemPaged["_models.ChatRoomMember"]: + """Get room members. + + Get room members. + + :param room_id: Room identifier. Required. + :type room_id: str + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatRoomMember + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatRoomMember] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatRoomMember]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_room_members_request( + room_id=room_id, + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatRoomMember], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @overload + async def create_or_replace_room_member( + self, + room_id: str, + user_id: str, + member: _models.ChatRoomMemberCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatRoomMember: + """Create or replace a room member. + + Create or replace a room member. + + :param room_id: Room identifier. Required. + :type room_id: str + :param user_id: User ID of the member. Required. + :type user_id: str + :param member: The resource instance. Required. + :type member: ~azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_or_replace_room_member( + self, + room_id: str, + user_id: str, + member: _types.ChatRoomMemberCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatRoomMember: + """Create or replace a room member. + + Create or replace a room member. + + :param room_id: Room identifier. Required. + :type room_id: str + :param user_id: User ID of the member. Required. + :type user_id: str + :param member: The resource instance. Required. + :type member: ~azure.messaging.webpubsubchatservice.types.ChatRoomMemberCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_or_replace_room_member( + self, + room_id: str, + user_id: str, + member: IO[bytes], + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatRoomMember: + """Create or replace a room member. + + Create or replace a room member. + + :param room_id: Room identifier. Required. + :type room_id: str + :param user_id: User ID of the member. Required. + :type user_id: str + :param member: The resource instance. Required. + :type member: IO[bytes] + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_or_replace_room_member( + self, + room_id: str, + user_id: str, + member: Union[_models.ChatRoomMemberCreateOrReplace, _types.ChatRoomMemberCreateOrReplace, IO[bytes]], + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatRoomMember: + """Create or replace a room member. + + Create or replace a room member. + + :param room_id: Room identifier. Required. + :type room_id: str + :param user_id: User ID of the member. Required. + :type user_id: str + :param member: The resource instance. Is either a ChatRoomMemberCreateOrReplace type or a + IO[bytes] type. Required. + :type member: ~azure.messaging.webpubsubchatservice.models.ChatRoomMemberCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatRoomMemberCreateOrReplace or IO[bytes] + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatRoomMember. The ChatRoomMember is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatRoomMember + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.ChatRoomMember] = kwargs.pop("cls", None) + + content_type = content_type or "application/json" + _content = None + if isinstance(member, (IOBase, bytes)): + _content = member + else: + _content = json.dumps(member, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_web_pub_sub_chat_service_create_or_replace_room_member_request( + room_id=room_id, + user_id=user_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + response_headers = {} + response_headers["ETag"] = self._deserialize("str", response.headers.get("ETag")) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatRoomMember, response.json()) + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def delete_room_member( + self, + room_id: str, + user_id: str, + *, + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> None: + """Delete a room member. + + Delete a room member. + + :param room_id: Room identifier. Required. + :type room_id: str + :param user_id: User ID of the member. Required. + :type user_id: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: None + :rtype: None + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + if match_condition == MatchConditions.IfNotModified: + error_map[412] = ResourceModifiedError + elif match_condition == MatchConditions.IfPresent: + error_map[412] = ResourceNotFoundError + elif match_condition == MatchConditions.IfMissing: + error_map[412] = ResourceExistsError + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[None] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_delete_room_member_request( + room_id=room_id, + user_id=user_id, + hub=self._config.hub, + etag=etag, + match_condition=match_condition, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_users(self, *, max_page_size: Optional[int] = None, **kwargs: Any) -> AsyncItemPaged["_models.ChatUser"]: + """List all users with pagination. + + List all users with pagination. + + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatUser + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatUser] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatUser]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_users_request( + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatUser], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def get_user(self, user_id: str, **kwargs: Any) -> _models.ChatUser: + """Get a user's profile. + + Get a user's profile. The response is a polymorphic ``ChatUser`` (e.g. ``HumanChatUser``) + selected by the ``kind`` discriminator. + + :param user_id: User identifier. Required. + :type user_id: str + :return: ChatUser. The ChatUser is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatUser + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.ChatUser] = kwargs.pop("cls", None) + + _request = build_web_pub_sub_chat_service_get_user_request( + user_id=user_id, + hub=self._config.hub, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.ChatUser, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_or_replace_user( + self, + user_id: str, + user: _models.ChatUserCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatUser: + """Create or replace a user. + + Create or replace a user. The request body is a polymorphic ``ChatUser`` (e.g. + ``HumanChatUser``) selected by the ``kind`` discriminator. + + :param user_id: User identifier. Required. + :type user_id: str + :param user: The resource instance. Required. + :type user: ~azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is + None. + :paramtype etag: str + :keyword match_condition: The match condition to use upon the etag. Default value is None. + :paramtype match_condition: ~azure.core.MatchConditions + :return: ChatUser. The ChatUser is compatible with MutableMapping + :rtype: ~azure.messaging.webpubsubchatservice.models.ChatUser + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_or_replace_user( + self, + user_id: str, + user: _types.ChatUserCreateOrReplace, + *, + content_type: str = "application/json", + etag: Optional[str] = None, + match_condition: Optional[MatchConditions] = None, + **kwargs: Any + ) -> _models.ChatUser: + """Create or replace a user. + + Create or replace a user. The request body is a polymorphic ``ChatUser`` (e.g. ``HumanChatUser``) selected by the ``kind`` discriminator. :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: ~azure.messaging.webpubsubchatservice.types.ChatUser + :param user: The resource instance. Required. + :type user: ~azure.messaging.webpubsubchatservice.types.ChatUserCreateOrReplace :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. Default value is "application/json". :paramtype content_type: str @@ -1825,7 +2733,7 @@ async def create_or_replace_user( async def create_or_replace_user( self, user_id: str, - resource: IO[bytes], + user: IO[bytes], *, content_type: str = "application/json", etag: Optional[str] = None, @@ -1839,8 +2747,8 @@ async def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Required. - :type resource: IO[bytes] + :param user: The resource instance. Required. + :type user: IO[bytes] :keyword content_type: Body Parameter content-type. Content type parameter for binary body. Default value is "application/json". :paramtype content_type: str @@ -1858,7 +2766,7 @@ async def create_or_replace_user( async def create_or_replace_user( self, user_id: str, - resource: Union[_models.ChatUser, _types.ChatUser, IO[bytes]], + user: Union[_models.ChatUserCreateOrReplace, _types.ChatUserCreateOrReplace, IO[bytes]], *, etag: Optional[str] = None, match_condition: Optional[MatchConditions] = None, @@ -1871,10 +2779,10 @@ async def create_or_replace_user( :param user_id: User identifier. Required. :type user_id: str - :param resource: The resource instance. Is either a ChatUser type or a IO[bytes] type. - Required. - :type resource: ~azure.messaging.webpubsubchatservice.models.ChatUser or - ~azure.messaging.webpubsubchatservice.types.ChatUser or IO[bytes] + :param user: The resource instance. Is either a ChatUserCreateOrReplace type or a IO[bytes] + type. Required. + :type user: ~azure.messaging.webpubsubchatservice.models.ChatUserCreateOrReplace or + ~azure.messaging.webpubsubchatservice.types.ChatUserCreateOrReplace or IO[bytes] :keyword etag: check if resource is changed. Set None to skip checking etag. Default value is None. :paramtype etag: str @@ -1906,10 +2814,10 @@ async def create_or_replace_user( content_type = content_type or "application/json" _content = None - if isinstance(resource, (IOBase, bytes)): - _content = resource + if isinstance(user, (IOBase, bytes)): + _content = user else: - _content = json.dumps(resource, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + _content = json.dumps(user, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore _request = build_web_pub_sub_chat_service_create_or_replace_user_request( user_id=user_id, @@ -2028,6 +2936,111 @@ async def delete_user( if cls: return cls(pipeline_response, None, {}) # type: ignore + @distributed_trace + @api_version_validation( + method_added_on="2026-08-01-preview", + params_added_on={"2026-08-01-preview": ["api_version", "hub", "user_id", "max_page_size", "accept"]}, + api_versions_list=["2026-08-01-preview"], + ) + def list_rooms_for_user( + self, user_id: str, *, max_page_size: Optional[int] = None, **kwargs: Any + ) -> AsyncItemPaged["_models.ChatUserRoom"]: + """List rooms for a user with pagination. + + List rooms for a user with pagination. + + :param user_id: User identifier. Required. + :type user_id: str + :keyword max_page_size: The maximum number of result items per page. Default value is None. + :paramtype max_page_size: int + :return: An iterator like instance of ChatUserRoom + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.messaging.webpubsubchatservice.models.ChatUserRoom] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.ChatUserRoom]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(next_link=None): + if not next_link: + + _request = build_web_pub_sub_chat_service_list_rooms_for_user_request( + user_id=user_id, + hub=self._config.hub, + max_page_size=max_page_size, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + else: + # make call to next link with the client's api-version + _parsed_next_link = urllib.parse.urlparse(next_link) + _next_request_params = case_insensitive_dict( + { + key: [urllib.parse.quote(v) for v in value] + for key, value in urllib.parse.parse_qs(_parsed_next_link.query).items() + } + ) + _next_request_params["api-version"] = self._config.api_version + _request = HttpRequest( + "GET", + urllib.parse.urljoin(next_link, _parsed_next_link.path), + headers=_headers, + params=_next_request_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url( + "self._config.endpoint", self._config.endpoint, "str", skip_quote=True + ), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.ChatUserRoom], + deserialized.get("value", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("nextLink") or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + _request = prepare_request(next_link) + + _stream = False + pipeline_response: PipelineResponse = await self._client._pipeline.run( # type: ignore # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + @distributed_trace_async async def _generate_client_token( self, diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/__init__.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/__init__.py index f899728a6f97..ea91b356fa91 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/__init__.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/__init__.py @@ -16,16 +16,27 @@ from ._models import ( # type: ignore ChatConversation, ChatMessage, + ChatMessageUpdate, ChatRole, + ChatRoleCreateOrReplace, ChatRoom, + ChatRoomCreateOrReplace, ChatRoomMember, + ChatRoomMemberCreateOrReplace, + ChatTopic, + ChatTopicCreateOrReplace, + ChatTopicUpdate, ChatUser, + ChatUserCreateOrReplace, + ChatUserRoom, HumanChatUser, + HumanChatUserCreateOrReplace, MessageContent, ) from ._enums import ( # type: ignore ChatPermission, + ChatTopicState, ChatUserKind, ) from ._patch import __all__ as _patch_all @@ -35,13 +46,24 @@ __all__ = [ "ChatConversation", "ChatMessage", + "ChatMessageUpdate", "ChatRole", + "ChatRoleCreateOrReplace", "ChatRoom", + "ChatRoomCreateOrReplace", "ChatRoomMember", + "ChatRoomMemberCreateOrReplace", + "ChatTopic", + "ChatTopicCreateOrReplace", + "ChatTopicUpdate", "ChatUser", + "ChatUserCreateOrReplace", + "ChatUserRoom", "HumanChatUser", + "HumanChatUserCreateOrReplace", "MessageContent", "ChatPermission", + "ChatTopicState", "ChatUserKind", ] __all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_enums.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_enums.py index 1a61c9616269..1e3b31bf8c7e 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_enums.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_enums.py @@ -27,6 +27,17 @@ class ChatPermission(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Allows a room operator to remove users from a room.""" +class ChatTopicState(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The lifecycle state of a chat topic.""" + + NORMAL = "Normal" + """The topic is active.""" + ARCHIVED = "Archived" + """The topic is archived.""" + SOFT_DELETED = "SoftDeleted" + """The topic has been soft deleted.""" + + class ChatUserKind(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Discriminator for the kind of chat user.""" diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_models.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_models.py index 68fa25ac834e..af7407bb8058 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_models.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/models/_models.py @@ -23,15 +23,15 @@ class ChatConversation(_Model): # pylint: disable=docstring-keyword-should-matc :ivar id: Conversation identifier. Required. :vartype id: str - :ivar parent_room: Parent room identifier. Required. - :vartype parent_room: str + :ivar room_id: Parent room identifier. Required. + :vartype room_id: str :ivar etag: The entity tag for this resource. Required. :vartype etag: str """ id: str = rest_field(visibility=["read"]) """Conversation identifier. Required.""" - parent_room: str = rest_field(name="parentRoom", visibility=["read", "create", "update", "delete", "query"]) + room_id: str = rest_field(name="parentRoom", visibility=["read", "create", "update", "delete", "query"]) """Parent room identifier. Required.""" etag: str = rest_field(visibility=["read"]) """The entity tag for this resource. Required.""" @@ -40,7 +40,7 @@ class ChatConversation(_Model): # pylint: disable=docstring-keyword-should-matc def __init__( self, *, - parent_room: str, + room_id: str, ) -> None: ... @overload @@ -99,6 +99,39 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ChatMessageUpdate(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when partially updating a message. + + :ivar created_by: User who created the message. + :vartype created_by: str + :ivar content: Message content. + :vartype content: ~azure.messaging.webpubsubchatservice.models.MessageContent + """ + + created_by: Optional[str] = rest_field(name="createdBy", visibility=["read", "create", "update", "delete", "query"]) + """User who created the message.""" + content: Optional["_models.MessageContent"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Message content.""" + + @overload + def __init__( + self, + *, + created_by: Optional[str] = None, + content: Optional["_models.MessageContent"] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class ChatRole(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Represents a chat role. A role name must start with 'user.' or 'room.' prefix. A role must contain either user permissions or room permissions, but not both. @@ -140,6 +173,38 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ChatRoleCreateOrReplace(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a role. + + :ivar permissions: Permissions associated with the role. Do not mix user permissions and room + permissions in one role. Required. + :vartype permissions: list[str or ~azure.messaging.webpubsubchatservice.models.ChatPermission] + """ + + permissions: list[Union[str, "_models.ChatPermission"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Permissions associated with the role. Do not mix user permissions and room permissions in one + role. Required.""" + + @overload + def __init__( + self, + *, + permissions: list[Union[str, "_models.ChatPermission"]], + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class ChatRoom(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Represents a chat room. @@ -180,6 +245,34 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ChatRoomCreateOrReplace(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a room. + + :ivar title: Room title. Required. + :vartype title: str + """ + + title: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Room title. Required.""" + + @overload + def __init__( + self, + *, + title: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class ChatRoomMember(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Represents a room member. @@ -216,6 +309,163 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ChatRoomMemberCreateOrReplace(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a room member. + + :ivar role_name: Room role assigned to the user within this room. Required. + :vartype role_name: str + """ + + role_name: str = rest_field(name="roleName", visibility=["read", "create", "update", "delete", "query"]) + """Room role assigned to the user within this room. Required.""" + + @overload + def __init__( + self, + *, + role_name: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ChatTopic(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Represents a chat topic in a room. + + :ivar id: Topic identifier unique within the room. Required. + :vartype id: str + :ivar title: Topic title. Required. + :vartype title: str + :ivar created_at: Timestamp when the topic was created. Required. + :vartype created_at: ~datetime.datetime + :ivar created_by: User who created the topic. Required. + :vartype created_by: str + :ivar state: Current lifecycle state of the topic. Required. Known values are: "Normal", + "Archived", and "SoftDeleted". + :vartype state: str or ~azure.messaging.webpubsubchatservice.models.ChatTopicState + :ivar conversation_id: Conversation associated with this topic. Required. + :vartype conversation_id: str + :ivar etag: The entity tag for this resource. Required. + :vartype etag: str + """ + + id: str = rest_field(visibility=["read"]) + """Topic identifier unique within the room. Required.""" + title: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Topic title. Required.""" + created_at: datetime.datetime = rest_field(name="createdAt", visibility=["read"], format="rfc3339") + """Timestamp when the topic was created. Required.""" + created_by: str = rest_field(name="createdBy", visibility=["read"]) + """User who created the topic. Required.""" + state: Union[str, "_models.ChatTopicState"] = rest_field(visibility=["read", "update"]) + """Current lifecycle state of the topic. Required. Known values are: \"Normal\", \"Archived\", and + \"SoftDeleted\".""" + conversation_id: str = rest_field(name="conversationId", visibility=["read"]) + """Conversation associated with this topic. Required.""" + etag: str = rest_field(visibility=["read"]) + """The entity tag for this resource. Required.""" + + @overload + def __init__( + self, + *, + title: str, + state: Union[str, "_models.ChatTopicState"], + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ChatTopicCreateOrReplace(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a topic. + + :ivar title: Topic title. Required. + :vartype title: str + :ivar state: Current lifecycle state of the topic. Known values are: "Normal", "Archived", and + "SoftDeleted". + :vartype state: str or ~azure.messaging.webpubsubchatservice.models.ChatTopicState + """ + + title: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Topic title. Required.""" + state: Optional[Union[str, "_models.ChatTopicState"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Current lifecycle state of the topic. Known values are: \"Normal\", \"Archived\", and + \"SoftDeleted\".""" + + @overload + def __init__( + self, + *, + title: str, + state: Optional[Union[str, "_models.ChatTopicState"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ChatTopicUpdate(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when partially updating a topic. + + :ivar title: Topic title. + :vartype title: str + :ivar state: Current lifecycle state of the topic. Known values are: "Normal", "Archived", and + "SoftDeleted". + :vartype state: str or ~azure.messaging.webpubsubchatservice.models.ChatTopicState + """ + + title: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Topic title.""" + state: Optional[Union[str, "_models.ChatTopicState"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Current lifecycle state of the topic. Known values are: \"Normal\", \"Archived\", and + \"SoftDeleted\".""" + + @overload + def __init__( + self, + *, + title: Optional[str] = None, + state: Optional[Union[str, "_models.ChatTopicState"]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class ChatUser(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Represents a user profile in the chat system. This is a discriminated base type; concrete payloads are selected by the ``kind`` field (e.g. ``HumanChatUser``). @@ -262,6 +512,86 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) +class ChatUserCreateOrReplace(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a user. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + HumanChatUserCreateOrReplace + + :ivar kind: The kind of user. Required. "Human" + :vartype kind: str or ~azure.messaging.webpubsubchatservice.models.ChatUserKind + :ivar nickname: User's display nickname. Required. + :vartype nickname: str + """ + + __mapping__: dict[str, _Model] = {} + kind: str = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) + """The kind of user. Required. \"Human\"""" + nickname: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """User's display nickname. Required.""" + + @overload + def __init__( + self, + *, + kind: str, + nickname: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class ChatUserRoom(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only + """Represents a read-only room projection used to list rooms associated with a user. This model + enables the standard resource operation to derive the + ``/api/hubs/{hub}/chat/users/{userId}/rooms`` collection path because ``ChatRoom`` is a child + of ``Hub``, not ``ChatUser``. This model does not define an independent room lifecycle. + + :ivar id: Room identifier. Required. + :vartype id: str + :ivar title: Room title. Required. + :vartype title: str + :ivar default_conversation: Default conversation ID for this room. Required. + :vartype default_conversation: str + :ivar etag: The entity tag for this resource. Required. + :vartype etag: str + """ + + id: str = rest_field(visibility=["read"]) + """Room identifier. Required.""" + title: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Room title. Required.""" + default_conversation: str = rest_field(name="defaultConversation", visibility=["read"]) + """Default conversation ID for this room. Required.""" + etag: str = rest_field(visibility=["read"]) + """The entity tag for this resource. Required.""" + + @overload + def __init__( + self, + *, + title: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class GenerateClientTokenResponse(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Response containing a Web PubSub client access token. @@ -330,6 +660,44 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.kind = ChatUserKind.HUMAN # type: ignore +class HumanChatUserCreateOrReplace( + ChatUserCreateOrReplace, discriminator="Human" +): # pylint: disable=docstring-keyword-should-match-keyword-only + """Properties supplied when creating or replacing a human user. + + :ivar kind: Required. A human end-user. + :vartype kind: str or ~azure.messaging.webpubsubchatservice.models.HUMAN + :ivar role_name: Global user role assigned to the user. Must start with ``user.``. Required. + :vartype role_name: str + :ivar nickname: User's display nickname. Required. + :vartype nickname: str + """ + + kind: Literal[ChatUserKind.HUMAN] = rest_discriminator(name="kind", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. A human end-user.""" + role_name: str = rest_field(name="roleName", visibility=["read", "create", "update", "delete", "query"]) + """Global user role assigned to the user. Must start with ``user.``. Required.""" + + @overload + def __init__( + self, + *, + role_name: str, + nickname: str, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.kind = ChatUserKind.HUMAN # type: ignore + + class MessageContent(_Model): # pylint: disable=docstring-keyword-should-match-keyword-only """Message content body. diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/types.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/types.py index c738d721fea3..14755098e183 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/types.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/azure/messaging/webpubsubchatservice/types.py @@ -12,125 +12,110 @@ from .models._enums import ChatUserKind if TYPE_CHECKING: - from .models import ChatPermission + from .models import ChatPermission, ChatTopicState -class ChatMessage(TypedDict, total=False): - """Represents a chat message. +class ChatMessageUpdate(TypedDict, total=False): + """Properties supplied when partially updating a message. - :ivar id: Message identifier. Required. - :vartype id: str - :ivar createdBy: User who created the message. Required. + :ivar createdBy: User who created the message. :vartype createdBy: str - :ivar content: Message content. Required. + :ivar content: Message content. :vartype content: "MessageContent" - :ivar createdAt: Timestamp when the message was created. Required. - :vartype createdAt: str - :ivar etag: The entity tag for this resource. Required. - :vartype etag: str """ - id: Required[str] - """Message identifier. Required.""" - createdBy: Required[str] - """User who created the message. Required.""" - content: Required["MessageContent"] - """Message content. Required.""" - createdAt: Required[str] - """Timestamp when the message was created. Required.""" - etag: Required[str] - """The entity tag for this resource. Required.""" + createdBy: str + """User who created the message.""" + content: "MessageContent" + """Message content.""" -class ChatRole(TypedDict, total=False): - """Represents a chat role. A role name must start with 'user.' or 'room.' prefix. A role must - contain either user permissions or room permissions, but not both. +class ChatRoleCreateOrReplace(TypedDict, total=False): + """Properties supplied when creating or replacing a role. - :ivar name: Role name. Must start with 'user.' or 'room.' prefix. Required. - :vartype name: str :ivar permissions: Permissions associated with the role. Do not mix user permissions and room permissions in one role. Required. :vartype permissions: list[Union[str, "ChatPermission"]] - :ivar etag: The entity tag for this resource. Required. - :vartype etag: str """ - name: Required[str] - """Role name. Must start with 'user.' or 'room.' prefix. Required.""" permissions: Required[list[Union[str, "ChatPermission"]]] """Permissions associated with the role. Do not mix user permissions and room permissions in one role. Required.""" - etag: Required[str] - """The entity tag for this resource. Required.""" -class ChatRoom(TypedDict, total=False): - """Represents a chat room. +class ChatRoomCreateOrReplace(TypedDict, total=False): + """Properties supplied when creating or replacing a room. - :ivar id: Room identifier. Required. - :vartype id: str :ivar title: Room title. Required. :vartype title: str - :ivar defaultConversation: Default conversation ID for this room. Required. - :vartype defaultConversation: str - :ivar etag: The entity tag for this resource. Required. - :vartype etag: str """ - id: Required[str] - """Room identifier. Required.""" title: Required[str] """Room title. Required.""" - defaultConversation: Required[str] - """Default conversation ID for this room. Required.""" - etag: Required[str] - """The entity tag for this resource. Required.""" -class ChatRoomMember(TypedDict, total=False): - """Represents a room member. +class ChatRoomMemberCreateOrReplace(TypedDict, total=False): + """Properties supplied when creating or replacing a room member. - :ivar userId: User ID of the member. Required. - :vartype userId: str :ivar roleName: Room role assigned to the user within this room. Required. :vartype roleName: str - :ivar etag: The entity tag for this resource. Required. - :vartype etag: str """ - userId: Required[str] - """User ID of the member. Required.""" roleName: Required[str] """Room role assigned to the user within this room. Required.""" - etag: Required[str] - """The entity tag for this resource. Required.""" -class HumanChatUser(TypedDict, total=False): - """A human end-user, identified by a user role. +class ChatTopicCreateOrReplace(TypedDict, total=False): + """Properties supplied when creating or replacing a topic. + + :ivar title: Topic title. Required. + :vartype title: str + :ivar state: Current lifecycle state of the topic. Known values are: "Normal", "Archived", and + "SoftDeleted". + :vartype state: Union[str, "ChatTopicState"] + """ + + title: Required[str] + """Topic title. Required.""" + state: Union[str, "ChatTopicState"] + """Current lifecycle state of the topic. Known values are: \"Normal\", \"Archived\", and + \"SoftDeleted\".""" + + +class ChatTopicUpdate(TypedDict, total=False): + """Properties supplied when partially updating a topic. + + :ivar title: Topic title. + :vartype title: str + :ivar state: Current lifecycle state of the topic. Known values are: "Normal", "Archived", and + "SoftDeleted". + :vartype state: Union[str, "ChatTopicState"] + """ + + title: str + """Topic title.""" + state: Union[str, "ChatTopicState"] + """Current lifecycle state of the topic. Known values are: \"Normal\", \"Archived\", and + \"SoftDeleted\".""" + + +class HumanChatUserCreateOrReplace(TypedDict, total=False): + """Properties supplied when creating or replacing a human user. - :ivar id: User identifier. Required. - :vartype id: str - :ivar nickname: User's display nickname. Required. - :vartype nickname: str - :ivar etag: The entity tag for this resource. Required. - :vartype etag: str :ivar kind: Required. A human end-user. :vartype kind: Literal[ChatUserKind.HUMAN] :ivar roleName: Global user role assigned to the user. Must start with ``user.``. Required. :vartype roleName: str + :ivar nickname: User's display nickname. Required. + :vartype nickname: str """ - id: Required[str] - """User identifier. Required.""" - nickname: Required[str] - """User's display nickname. Required.""" - etag: Required[str] - """The entity tag for this resource. Required.""" kind: Required[Literal[ChatUserKind.HUMAN]] """Required. A human end-user.""" roleName: Required[str] """Global user role assigned to the user. Must start with ``user.``. Required.""" + nickname: Required[str] + """User's display nickname. Required.""" class MessageContent(TypedDict, total=False): @@ -148,4 +133,4 @@ class MessageContent(TypedDict, total=False): """Binary content (base64 encoded).""" -ChatUser = Union[HumanChatUser] +ChatUserCreateOrReplace = Union[HumanChatUserCreateOrReplace] diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_role.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_role.py deleted file mode 100644 index 6ced1c28454a..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_role.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python create_or_replace_role.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.create_or_replace_role( - role_name="room.moderator", - resource={"permissions": ["room.history", "room.remove_user", "room.publish_message"]}, - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/CreateOrReplaceRole.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room.py deleted file mode 100644 index 1232458595a1..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python create_or_replace_room.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.create_or_replace_room( - room_id="room1", - resource={"title": "General Chat"}, - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/CreateOrReplaceRoom.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room_member.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room_member.py deleted file mode 100644 index 63df38fe4a11..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_room_member.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python create_or_replace_room_member.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.create_or_replace_room_member( - room_id="room1", - user_id="user1", - resource={"roleName": "room.member"}, - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/CreateOrReplaceRoomMember.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_user.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_user.py deleted file mode 100644 index f41d09be86b9..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/create_or_replace_user.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python create_or_replace_user.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.create_or_replace_user( - user_id="user1", - resource={"kind": "Human", "nickname": "John", "roleName": "user.normal"}, - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/CreateOrReplaceUser.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_message.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_message.py deleted file mode 100644 index 8047a19cf905..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_message.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python delete_message.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - client.delete_message( - conversation_id="c.room1.abcd1234", - message_id="123", - ) - - -# x-ms-original-file: 2026-02-01-preview/DeleteMessage.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_role.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_role.py deleted file mode 100644 index 8b6d5dd18751..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_role.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python delete_role.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - client.delete_role( - role_name="room.moderator", - ) - - -# x-ms-original-file: 2026-02-01-preview/DeleteRole.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room.py deleted file mode 100644 index 005baa4924b0..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python delete_room.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - client.delete_room( - room_id="room1", - ) - - -# x-ms-original-file: 2026-02-01-preview/DeleteRoom.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room_member.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room_member.py deleted file mode 100644 index b6dd41e89d34..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_room_member.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python delete_room_member.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - client.delete_room_member( - room_id="room1", - user_id="user1", - ) - - -# x-ms-original-file: 2026-02-01-preview/DeleteRoomMember.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_user.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_user.py deleted file mode 100644 index 2dea2d5b3bdd..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/delete_user.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python delete_user.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - client.delete_user( - user_id="user1", - ) - - -# x-ms-original-file: 2026-02-01-preview/DeleteUser.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_conversation.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_conversation.py deleted file mode 100644 index 3a0c9ba6c6a8..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_conversation.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python get_conversation.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.get_conversation( - conversation_id="c.room1.abcd1234", - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/GetConversation.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_role.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_role.py deleted file mode 100644 index 599b5868a943..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_role.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python get_role.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.get_role( - role_name="room.operator", - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/GetRole.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_room.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_room.py deleted file mode 100644 index a64f9fc6642a..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_room.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python get_room.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.get_room( - room_id="room1", - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/GetRoom.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_user.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_user.py deleted file mode 100644 index edb48e9c3c33..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/get_user.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python get_user.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.get_user( - user_id="user1", - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/GetUser.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_messages.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_messages.py deleted file mode 100644 index 60df639067b5..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_messages.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python list_messages.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.list_messages( - conversation_id="c.room1.abcd1234", - ) - for item in response: - print(item) - - -# x-ms-original-file: 2026-02-01-preview/ListMessages.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_roles.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_roles.py deleted file mode 100644 index f3dfaddc7950..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_roles.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python list_roles.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.list_roles() - for item in response: - print(item) - - -# x-ms-original-file: 2026-02-01-preview/ListRoles.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_room_members.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_room_members.py deleted file mode 100644 index 2684841869b8..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/list_room_members.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python list_room_members.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.list_room_members( - room_id="room1", - ) - for item in response: - print(item) - - -# x-ms-original-file: 2026-02-01-preview/ListRoomMembers.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/update_message.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/update_message.py deleted file mode 100644 index 3fd09e877ee4..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_samples/update_message.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# Code generated by Microsoft (R) Python Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is regenerated. -# -------------------------------------------------------------------------- - -from azure.identity import DefaultAzureCredential - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - -""" -# PREREQUISITES - pip install azure-identity - pip install azure-messaging-webpubsubchatservice -# USAGE - python update_message.py - - Before run the sample, please set the values of the client ID, tenant ID and client secret - of the AAD application as environment variables: AZURE_CLIENT_ID, AZURE_TENANT_ID, - AZURE_CLIENT_SECRET. For more info about how to get the value, please see: - https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal -""" - - -def main(): - client = WebPubSubChatServiceClient( - endpoint="ENDPOINT", - credential=DefaultAzureCredential(), - hub="HUB", - ) - - response = client.update_message( - conversation_id="c.room1.abcd1234", - message_id="123", - resource={"content": {"text": "Updated message text"}, "createdBy": "user1"}, - ) - print(response) - - -# x-ms-original-file: 2026-02-01-preview/UpdateMessage.json -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service.py index 12a168e8992f..44b1ea15f37d 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service.py @@ -53,13 +53,7 @@ def test_update_message(self, webpubsubchatservice_endpoint): response = client.update_message( conversation_id="str", message_id="str", - resource={ - "content": {"binary": bytes("bytes", encoding="utf-8"), "text": "str"}, - "createdAt": "2020-02-20 00:00:00", - "createdBy": "str", - "etag": "str", - "id": "str", - }, + message={"content": {"binary": bytes("bytes", encoding="utf-8"), "text": "str"}, "createdBy": "str"}, ) # please add some check logic here by yourself @@ -91,7 +85,7 @@ def test_create_or_replace_role(self, webpubsubchatservice_endpoint): client = self.create_client(endpoint=webpubsubchatservice_endpoint) response = client.create_or_replace_role( role_name="str", - resource={"etag": "str", "name": "str", "permissions": ["str"]}, + role={"permissions": ["str"]}, ) # please add some check logic here by yourself @@ -108,13 +102,22 @@ def test_delete_role(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_list_rooms(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_rooms() + result = [r for r in response] + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy def test_create_or_replace_room(self, webpubsubchatservice_endpoint): client = self.create_client(endpoint=webpubsubchatservice_endpoint) response = client.create_or_replace_room( room_id="str", - resource={"defaultConversation": "str", "etag": "str", "id": "str", "title": "str"}, + room={"title": "str"}, ) # please add some check logic here by yourself @@ -142,6 +145,67 @@ def test_delete_room(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_list_topics(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_topics( + room_id="str", + ) + result = [r for r in response] + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_create_or_replace_topic(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.create_or_replace_topic( + room_id="str", + topic_id="str", + topic={"title": "str", "state": "str"}, + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_get_topic(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.get_topic( + room_id="str", + topic_id="str", + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_update_topic(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.update_topic( + room_id="str", + topic_id="str", + topic={"state": "str", "title": "str"}, + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_delete_topic(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.delete_topic( + room_id="str", + topic_id="str", + ) + + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy def test_list_room_members(self, webpubsubchatservice_endpoint): @@ -160,7 +224,7 @@ def test_create_or_replace_room_member(self, webpubsubchatservice_endpoint): response = client.create_or_replace_room_member( room_id="str", user_id="str", - resource={"etag": "str", "roleName": "str", "userId": "str"}, + member={"roleName": "str"}, ) # please add some check logic here by yourself @@ -178,6 +242,15 @@ def test_delete_room_member(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_list_users(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_users() + result = [r for r in response] + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy def test_get_user(self, webpubsubchatservice_endpoint): @@ -195,7 +268,7 @@ def test_create_or_replace_user(self, webpubsubchatservice_endpoint): client = self.create_client(endpoint=webpubsubchatservice_endpoint) response = client.create_or_replace_user( user_id="str", - resource={"etag": "str", "id": "str", "kind": "Human", "nickname": "str", "roleName": "str"}, + user={"kind": "Human", "nickname": "str", "roleName": "str"}, ) # please add some check logic here by yourself @@ -211,3 +284,14 @@ def test_delete_user(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy + def test_list_rooms_for_user(self, webpubsubchatservice_endpoint): + client = self.create_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_rooms_for_user( + user_id="str", + ) + result = [r for r in response] + # please add some check logic here by yourself + # ... diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service_async.py index d523fbf6b6df..5e13224d43f8 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service_async.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/generated_tests/test_web_pub_sub_chat_service_async.py @@ -54,13 +54,7 @@ async def test_update_message(self, webpubsubchatservice_endpoint): response = await client.update_message( conversation_id="str", message_id="str", - resource={ - "content": {"binary": bytes("bytes", encoding="utf-8"), "text": "str"}, - "createdAt": "2020-02-20 00:00:00", - "createdBy": "str", - "etag": "str", - "id": "str", - }, + message={"content": {"binary": bytes("bytes", encoding="utf-8"), "text": "str"}, "createdBy": "str"}, ) # please add some check logic here by yourself @@ -92,7 +86,7 @@ async def test_create_or_replace_role(self, webpubsubchatservice_endpoint): client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) response = await client.create_or_replace_role( role_name="str", - resource={"etag": "str", "name": "str", "permissions": ["str"]}, + role={"permissions": ["str"]}, ) # please add some check logic here by yourself @@ -109,13 +103,22 @@ async def test_delete_role(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_list_rooms(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_rooms() + result = [r async for r in response] + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy_async async def test_create_or_replace_room(self, webpubsubchatservice_endpoint): client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) response = await client.create_or_replace_room( room_id="str", - resource={"defaultConversation": "str", "etag": "str", "id": "str", "title": "str"}, + room={"title": "str"}, ) # please add some check logic here by yourself @@ -143,6 +146,67 @@ async def test_delete_room(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_list_topics(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_topics( + room_id="str", + ) + result = [r async for r in response] + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_create_or_replace_topic(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = await client.create_or_replace_topic( + room_id="str", + topic_id="str", + topic={"title": "str", "state": "str"}, + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_get_topic(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = await client.get_topic( + room_id="str", + topic_id="str", + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_update_topic(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = await client.update_topic( + room_id="str", + topic_id="str", + topic={"state": "str", "title": "str"}, + ) + + # please add some check logic here by yourself + # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_delete_topic(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = await client.delete_topic( + room_id="str", + topic_id="str", + ) + + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy_async async def test_list_room_members(self, webpubsubchatservice_endpoint): @@ -161,7 +225,7 @@ async def test_create_or_replace_room_member(self, webpubsubchatservice_endpoint response = await client.create_or_replace_room_member( room_id="str", user_id="str", - resource={"etag": "str", "roleName": "str", "userId": "str"}, + member={"roleName": "str"}, ) # please add some check logic here by yourself @@ -179,6 +243,15 @@ async def test_delete_room_member(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_list_users(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_users() + result = [r async for r in response] + # please add some check logic here by yourself + # ... + @WebPubSubChatServicePreparer() @recorded_by_proxy_async async def test_get_user(self, webpubsubchatservice_endpoint): @@ -196,7 +269,7 @@ async def test_create_or_replace_user(self, webpubsubchatservice_endpoint): client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) response = await client.create_or_replace_user( user_id="str", - resource={"etag": "str", "id": "str", "kind": "Human", "nickname": "str", "roleName": "str"}, + user={"kind": "Human", "nickname": "str", "roleName": "str"}, ) # please add some check logic here by yourself @@ -212,3 +285,14 @@ async def test_delete_user(self, webpubsubchatservice_endpoint): # please add some check logic here by yourself # ... + + @WebPubSubChatServicePreparer() + @recorded_by_proxy_async + async def test_list_rooms_for_user(self, webpubsubchatservice_endpoint): + client = self.create_async_client(endpoint=webpubsubchatservice_endpoint) + response = client.list_rooms_for_user( + user_id="str", + ) + result = [r async for r in response] + # please add some check logic here by yourself + # ... diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/pyproject.toml b/sdk/webpubsub/azure-messaging-webpubsubchatservice/pyproject.toml index 2c5f04f55ba8..ccf1ee1e2406 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/pyproject.toml +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/pyproject.toml @@ -48,6 +48,7 @@ version = {attr = "azure.messaging.webpubsubchatservice._version.VERSION"} readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} [tool.setuptools.packages.find] +include = ["azure.messaging.webpubsubchatservice*"] exclude = [ "tests*", "generated_tests*", diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md new file mode 100644 index 000000000000..87f8b3d2ab03 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/README.md @@ -0,0 +1,98 @@ +# Azure Web PubSub Chat samples + +Use the [package README](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md) for installation, authentication, and +Chat hub prerequisites. + +## Request-body styles + +Operations with a JSON request body support three styles: + +- **Model-based (recommended for getting started):** construct a class from + `azure.messaging.webpubsubchatservice.models`, using Python names such as + `role_name`. Models provide attribute access, type hints, and SDK-managed + serialization. See the model-based operation examples below and `model_based` + in the [three-style comparison](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py). +- **Typed dictionary:** annotate a dictionary with a `TypedDict` from + `azure.messaging.webpubsubchatservice.types`, using JSON names such as + `roleName`. A type checker can check its keys and value types. See + `typed_dictionary` in the [three-style comparison](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py). +- **Plain dictionary:** pass a JSON-shaped dictionary directly, without an + explicit `TypedDict` annotation. See `plain_dictionary` in the + [three-style comparison](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py). + +For example, these request bodies express the same room membership: + +```python +from azure.messaging.webpubsubchatservice import models, types + +model = models.ChatRoomMemberCreateOrReplace(role_name="room.member") +typed: types.ChatRoomMemberCreateOrReplace = {"roleName": "room.member"} +plain = {"roleName": "room.member"} +``` + +All three styles call the same client methods. A typed dictionary is an ordinary +dictionary at runtime; its annotation does not add runtime validation. These +examples use "JSON" to mean a dictionary of JSON fields, not a JSON-encoded string. +Dictionary literals can also be checked against the method's type hints. + +GET, LIST, and DELETE operations in this client have no JSON request body, so +there is no request-body style to choose. GET and LIST still return model +instances or paged models; their examples are not duplicated by input style. + +## Scenario samples + +| Sample | Purpose | +| --- | --- | +| [Authentication](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_authentication.py) | Connection string, access key, and Microsoft Entra ID authentication. | +| [Client access credentials](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py) / [async](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py) | Generate credentials for a Chat WebSocket client. | +| [Roles and permissions](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py) / [async](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py) | Create or replace a role using a model and permission enums. | +| [Message management](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_management.py) | Update and delete persisted messages. | +| [Service errors](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_errors.py) | Handle and re-raise an HTTP service error. | +| [Logging](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_logging.py) | Enable SDK HTTP logging. | +| [README snippet sources](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py) | Independent functions used by the README, not a standalone program. | + +For runnable scenario samples, follow the environment-variable instructions +in each file and the [README run instructions](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/README.md#run-the-examples). +Replace resource ID placeholders such as `` and `` +with existing resource identifiers before running the examples. +Some samples create, replace, or delete resources; use sample-specific +identifiers and review each file before running it. + +## Complete operation index + +The model-based operation examples expose a `sample(client)` function. Supply an +authenticated client as shown in the [authentication sample](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_authentication.py) +and replace the example identifiers with your own. These functions illustrate +API usage; they are not standalone command-line programs. + +Membership examples assume an existing room, user, and `room.member` role. User +creation assumes an existing `user.member` role. Topic examples assume an +existing room; update examples assume an existing topic or persisted message. +The topic update example reads the current ETag before issuing a conditional +update. No extra resource environment variables or shared runner are needed. + +Create/replace operations can overwrite existing resources; delete operations +remove the specified resource (topic deletion is a soft delete). Use isolated +sample resources and review each operation before running it. The samples do +not create unrelated prerequisites or send WebSocket messages. + +Standalone generated examples are not included. The handwritten examples below +cover every service operation; use the three-style comparison for dictionary inputs. + +| Operation | Handwritten example | +| --- | --- | +| `get_client_access_token` | [Sync](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py) / [async](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py) | +| `create_or_replace_role` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_role.py) | +| `create_or_replace_room` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room.py) | +| `create_or_replace_room_member` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room_member.py) / [dictionary styles](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py) | +| `create_or_replace_user` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_user.py) | +| `create_or_replace_topic` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_topic.py) / [typed dictionary replacement](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py) | +| `update_topic` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_topic.py) | +| `update_message` | [Model request](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_message.py) | +| `get_role`, `get_room`, `get_user`, `get_topic`, `get_conversation` | [Read operation functions](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_read_operations.py) | +| `list_roles`, `list_rooms`, `list_room_members`, `list_users`, `list_topics`, `list_rooms_for_user`, `list_messages` | [List operation functions](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_read_operations.py) | +| `delete_role`, `delete_room`, `delete_room_member`, `delete_user`, `delete_topic`, `delete_message` | [Delete operation functions](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_delete_operations.py) | + +The synchronous and asynchronous clients expose the same service operations. +Await asynchronous non-paging calls, and use `async for` to iterate asynchronous +paged results. diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py index 781283c8fd1d..674b041f7d47 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access.py @@ -6,7 +6,6 @@ def client_access(): - # [START client_access] import os from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient @@ -15,11 +14,12 @@ def client_access(): connection_string, os.environ.get("WPS_CHAT_HUB", "test_hub"), ) as client: + # [START client_access] access = client.get_client_access_token(user_id="sample-user") # Give access["url"] to the intended client to connect; it includes the access token. # Print only the token-free base URL here. Do not log access["url"]. print(access["baseUrl"]) - # [END client_access] + # [END client_access] def main(): diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py index 799072d2bf61..58e50253b88b 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_client_access_async.py @@ -15,14 +15,16 @@ async def main(): from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient client = WebPubSubChatServiceClient.from_connection_string( - connection_string, + os.environ["WPS_CHAT_CONNECTION_STRING"], os.environ.get("WPS_CHAT_HUB", "test_hub"), ) try: + # [START client_access_async] access = await client.get_client_access_token(user_id="sample-user") # Give access["url"] to the intended client to connect; it includes the access token. # Print only the token-free base URL here. Do not log access["url"]. print(access["baseUrl"]) + # [END client_access_async] finally: await client.close() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_role.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_role.py new file mode 100644 index 000000000000..f583cd2f7dba --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_role.py @@ -0,0 +1,16 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Create or replace a sample-specific room role. Existing permissions are replaced.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatPermission, ChatRoleCreateOrReplace + + +def sample(client: WebPubSubChatServiceClient) -> None: + role = client.create_or_replace_role( + "room.sample", + ChatRoleCreateOrReplace(permissions=[ChatPermission.ROOM_HISTORY, ChatPermission.ROOM_PUBLISH_MESSAGE]), + ) + print(role.name, role.permissions) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room.py new file mode 100644 index 000000000000..110ba2d7dc7a --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room.py @@ -0,0 +1,13 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Create or replace a sample-specific room.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatRoomCreateOrReplace + + +def sample(client: WebPubSubChatServiceClient) -> None: + room = client.create_or_replace_room("sample-room", ChatRoomCreateOrReplace(title="Sample room")) + print(room.id, room.title, room.default_conversation) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room_member.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room_member.py new file mode 100644 index 000000000000..02a7a0eed05d --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_room_member.py @@ -0,0 +1,17 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Add an existing user to a room, or replace their room role.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatRoomMemberCreateOrReplace + + +def sample(client: WebPubSubChatServiceClient) -> None: + member = client.create_or_replace_room_member( + "sample-room", + "sample-user", + ChatRoomMemberCreateOrReplace(role_name="room.member"), + ) + print(member.user_id, member.role_name) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_topic.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_topic.py new file mode 100644 index 000000000000..494718e7de3c --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_topic.py @@ -0,0 +1,17 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Create or replace a sample-specific topic in an existing room.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatTopicCreateOrReplace + + +def sample(client: WebPubSubChatServiceClient) -> None: + topic = client.create_or_replace_topic( + "sample-room", + "sample-topic", + ChatTopicCreateOrReplace(title="Sample topic"), + ) + print(topic.id, topic.conversation_id, topic.etag) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_user.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_user.py new file mode 100644 index 000000000000..1e2ab499c80e --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_create_or_replace_user.py @@ -0,0 +1,16 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Create or replace a sample-specific human user with an existing user role.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import HumanChatUserCreateOrReplace + + +def sample(client: WebPubSubChatServiceClient) -> None: + user = client.create_or_replace_user( + "sample-user", + HumanChatUserCreateOrReplace(nickname="Sample user", role_name="user.member"), + ) + print(user.id, user.kind) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_delete_operations.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_delete_operations.py new file mode 100644 index 000000000000..c9dcecae38a4 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_delete_operations.py @@ -0,0 +1,31 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Independent destructive examples. Replace identifiers with isolated sample resources.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient + + +def delete_role(client: WebPubSubChatServiceClient) -> None: + client.delete_role("room.sample") + + +def delete_room(client: WebPubSubChatServiceClient) -> None: + client.delete_room("sample-room") + + +def delete_room_member(client: WebPubSubChatServiceClient) -> None: + client.delete_room_member("sample-room", "sample-user") + + +def delete_user(client: WebPubSubChatServiceClient) -> None: + client.delete_user("sample-user") + + +def delete_topic(client: WebPubSubChatServiceClient) -> None: + client.delete_topic("sample-room", "sample-topic") + + +def delete_message(client: WebPubSubChatServiceClient) -> None: + client.delete_message("c.sample-room.sample-topic", "123456789") diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_errors.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_errors.py new file mode 100644 index 000000000000..aea4f59ea7d3 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_errors.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +import os + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient + + +def handle_service_errors(client): + # [START handle_service_errors] + from azure.core.exceptions import HttpResponseError + + try: + role = client.get_role("user.example") + print(role.name, role.permissions) + except HttpResponseError as error: + print(f"Chat service request failed with status {error.status_code}") + raise + # [END handle_service_errors] + + +def main(): + if not os.environ.get("WPS_CHAT_CONNECTION_STRING"): + print("Set WPS_CHAT_CONNECTION_STRING to run this sample.") + return + with WebPubSubChatServiceClient.from_connection_string( + os.environ["WPS_CHAT_CONNECTION_STRING"], os.environ.get("WPS_CHAT_HUB", "test_hub") + ) as client: + handle_service_errors(client) + + +if __name__ == "__main__": + main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_logging.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_logging.py new file mode 100644 index 000000000000..9c45013a3ac0 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_logging.py @@ -0,0 +1,40 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +import os + + +def configure_logging(): + # [START configure_logging] + import logging + import os + import sys + + from azure.identity import DefaultAzureCredential + from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient + + logger = logging.getLogger("azure") + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(stream=sys.stdout)) + + with DefaultAzureCredential() as credential: + with WebPubSubChatServiceClient( + os.environ["WPS_CHAT_ENDPOINT"], + os.environ.get("WPS_CHAT_HUB", "test_hub"), + credential, + logging_enable=True, + ) as client: + print(type(client).__name__) + # [END configure_logging] + + +def main(): + if not os.environ.get("WPS_CHAT_ENDPOINT"): + print("Set WPS_CHAT_ENDPOINT to run this sample.") + return + configure_logging() + + +if __name__ == "__main__": + main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history.py deleted file mode 100644 index 5d77b0e68164..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history.py +++ /dev/null @@ -1,26 +0,0 @@ -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------- -import os - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient - - -def main(): - required = ("WPS_CHAT_CONNECTION_STRING", "WPS_CHAT_CONVERSATION_ID") - missing = [name for name in required if not os.environ.get(name)] - if missing: - print(f"Set {', '.join(missing)} to run this sample.") - return - - with WebPubSubChatServiceClient.from_connection_string( - os.environ["WPS_CHAT_CONNECTION_STRING"], - os.environ.get("WPS_CHAT_HUB", "test_hub"), - ) as client: - for message in client.list_messages(os.environ["WPS_CHAT_CONVERSATION_ID"]): - print(message.id, message.created_by, message.content.text) - - -if __name__ == "__main__": - main() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history_async.py deleted file mode 100644 index 402ea87b518c..000000000000 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_history_async.py +++ /dev/null @@ -1,30 +0,0 @@ -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------- -import asyncio -import os - - -async def main(): - required = ("WPS_CHAT_CONNECTION_STRING", "WPS_CHAT_CONVERSATION_ID") - missing = [name for name in required if not os.environ.get(name)] - if missing: - print(f"Set {', '.join(missing)} to run this sample.") - return - - from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient - - client = WebPubSubChatServiceClient.from_connection_string( - os.environ["WPS_CHAT_CONNECTION_STRING"], - os.environ.get("WPS_CHAT_HUB", "test_hub"), - ) - try: - async for message in client.list_messages(os.environ["WPS_CHAT_CONVERSATION_ID"]): - print(message.id, message.created_by, message.content.text) - finally: - await client.close() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_management.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_management.py index 4755ee06d424..451fcb2ac1a9 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_management.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_message_management.py @@ -5,32 +5,24 @@ import os from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient -from azure.messaging.webpubsubchatservice.models import ChatMessage, MessageContent +from azure.messaging.webpubsubchatservice.models import ChatMessageUpdate, MessageContent def main(): - required = ( - "WPS_CHAT_CONNECTION_STRING", - "WPS_CHAT_CONVERSATION_ID", - "WPS_CHAT_MESSAGE_ID", - "WPS_CHAT_USER_ID", - ) - missing = [name for name in required if not os.environ.get(name)] - if missing: - print(f"Set {', '.join(missing)} to run this sample.") + if not os.environ.get("WPS_CHAT_CONNECTION_STRING"): + print("Set WPS_CHAT_CONNECTION_STRING to run this example.") return with WebPubSubChatServiceClient.from_connection_string( os.environ["WPS_CHAT_CONNECTION_STRING"], os.environ.get("WPS_CHAT_HUB", "test_hub"), ) as client: - conversation_id = os.environ["WPS_CHAT_CONVERSATION_ID"] - message_id = os.environ["WPS_CHAT_MESSAGE_ID"] + conversation_id = "" + message_id = "" client.update_message( conversation_id, message_id, - ChatMessage( - created_by=os.environ["WPS_CHAT_USER_ID"], + ChatMessageUpdate( content=MessageContent(text="Updated message"), ), ) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_read_operations.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_read_operations.py new file mode 100644 index 000000000000..ece5667a82ea --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_read_operations.py @@ -0,0 +1,67 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Independent read examples. Supply an authenticated client and existing identifiers.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient + + +def get_role(client: WebPubSubChatServiceClient) -> None: + role = client.get_role("room.sample") + print(role.name, role.permissions) + + +def get_room(client: WebPubSubChatServiceClient) -> None: + room = client.get_room("sample-room") + print(room.id, room.title) + + +def get_user(client: WebPubSubChatServiceClient) -> None: + user = client.get_user("sample-user") + print(user.id, user.kind) + + +def get_topic(client: WebPubSubChatServiceClient) -> None: + topic = client.get_topic("sample-room", "sample-topic") + print(topic.id, topic.title, topic.state) + + +def get_conversation(client: WebPubSubChatServiceClient) -> None: + conversation = client.get_conversation("c.sample-room.sample-topic") + print(conversation.id, conversation.room_id) + + +def list_roles(client: WebPubSubChatServiceClient) -> None: + for role in client.list_roles(max_page_size=10): + print(role.name) + + +def list_rooms(client: WebPubSubChatServiceClient) -> None: + for room in client.list_rooms(max_page_size=10): + print(room.id, room.title) + + +def list_room_members(client: WebPubSubChatServiceClient) -> None: + for member in client.list_room_members("sample-room", max_page_size=10): + print(member.user_id, member.role_name) + + +def list_users(client: WebPubSubChatServiceClient) -> None: + for user in client.list_users(max_page_size=10): + print(user.id, user.kind) + + +def list_topics(client: WebPubSubChatServiceClient) -> None: + for topic in client.list_topics("sample-room", max_page_size=10): + print(topic.id, topic.title) + + +def list_rooms_for_user(client: WebPubSubChatServiceClient) -> None: + for room in client.list_rooms_for_user("sample-user", max_page_size=10): + print(room.id, room.title) + + +def list_messages(client: WebPubSubChatServiceClient) -> None: + for message in client.list_messages("c.sample-room.sample-topic", max_page_size=10): + print(message.id, message.created_by, message.content) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py new file mode 100644 index 000000000000..f794f31ad937 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_readme_snippets.py @@ -0,0 +1,148 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Atomic README snippet sources; each function uses an authenticated client.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient + + +def list_roles(client: WebPubSubChatServiceClient) -> None: + # [START list_roles] + for role in client.list_roles(): + print(role.name) + # [END list_roles] + + +def create_user(client: WebPubSubChatServiceClient) -> None: + # [START create_user] + from azure.messaging.webpubsubchatservice.models import HumanChatUserCreateOrReplace + + user = client.create_or_replace_user( + "alice", + HumanChatUserCreateOrReplace(nickname="Alice", role_name="user.room_creator"), + ) + print(user.id, user.nickname) + # [END create_user] + + +def create_room(client: WebPubSubChatServiceClient) -> None: + # [START create_room] + from azure.messaging.webpubsubchatservice.models import ChatRoomCreateOrReplace + + room = client.create_or_replace_room("general", ChatRoomCreateOrReplace(title="General")) + print(room.id, room.default_conversation) + # [END create_room] + + +def add_room_member(client: WebPubSubChatServiceClient) -> None: + # [START add_room_member] + from azure.messaging.webpubsubchatservice.models import ChatRoomMemberCreateOrReplace + + member = client.create_or_replace_room_member( + "general", + "alice", + ChatRoomMemberCreateOrReplace(role_name="room.contributor"), + ) + print(member.user_id, member.role_name) + # [END add_room_member] + + +def list_messages(client: WebPubSubChatServiceClient) -> None: + # [START list_messages] + for message in client.list_messages(""): + print(message.id, message.created_by, message.content.text) + # [END list_messages] + + +def create_topic(client: WebPubSubChatServiceClient) -> None: + # [START create_topic] + from azure.messaging.webpubsubchatservice.models import ChatTopicCreateOrReplace + + topic = client.create_or_replace_topic( + "", + "", + ChatTopicCreateOrReplace(title="Announcements"), + ) + print(topic.id, topic.etag) + # [END create_topic] + + +def list_topics(client: WebPubSubChatServiceClient) -> None: + # [START list_topics] + for topic in client.list_topics("", max_page_size=10): + print(topic.id, topic.title, topic.conversation_id) + # [END list_topics] + + +def get_topic(client: WebPubSubChatServiceClient) -> None: + # [START get_topic] + topic = client.get_topic("", "") + print(topic.id, topic.title, topic.state, topic.etag) + # [END get_topic] + + +def archive_topic(client: WebPubSubChatServiceClient) -> None: + # [START archive_topic] + from azure.core import MatchConditions + from azure.messaging.webpubsubchatservice.models import ChatTopicUpdate, ChatTopicState + + topic = client.update_topic( + "", + "", + ChatTopicUpdate(state=ChatTopicState.ARCHIVED), + etag='""', + match_condition=MatchConditions.IfNotModified, + ) + print(topic.id, topic.state, topic.etag) + # [END archive_topic] + + +def delete_topic(client: WebPubSubChatServiceClient) -> None: + # [START delete_topic] + from azure.core import MatchConditions + + client.delete_topic( + "", + "", + etag='""', + match_condition=MatchConditions.IfNotModified, + ) + # [END delete_topic] + + +def list_rooms(client: WebPubSubChatServiceClient) -> None: + # [START list_rooms] + for room in client.list_rooms(max_page_size=10): + print(room.id, room.title) + # [END list_rooms] + + +def list_users(client: WebPubSubChatServiceClient) -> None: + # [START list_users] + for user in client.list_users(max_page_size=10): + print(user.id, user.kind) + # [END list_users] + + +def list_rooms_for_user(client: WebPubSubChatServiceClient) -> None: + # [START list_rooms_for_user] + for room in client.list_rooms_for_user(""): + print(room.id, room.title) + # [END list_rooms_for_user] + + +async def async_list_roles(endpoint: str, hub: str) -> None: + # [START async_list_roles] + from azure.identity.aio import DefaultAzureCredential + from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient + + credential = DefaultAzureCredential() + client = WebPubSubChatServiceClient(endpoint, hub, credential) + try: + async for role in client.list_roles(): + print(role.name) + finally: + await client.close() + await credential.close() + # [END async_list_roles] diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py new file mode 100644 index 000000000000..85c561410462 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_request_body_styles.py @@ -0,0 +1,38 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Three equivalent ways to add an existing user to a room with an existing room role. + +Each function accepts an authenticated client. Adapt the example identifiers +before calling one of the functions; all three create or replace the same membership. +""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient, models, types + + +def model_based(client: WebPubSubChatServiceClient) -> None: + member = client.create_or_replace_room_member( + "sample-room", + "sample-user", + models.ChatRoomMemberCreateOrReplace(role_name="room.member"), + ) + print(member.user_id, member.role_name) + + +def typed_dictionary(client: WebPubSubChatServiceClient) -> None: + resource: types.ChatRoomMemberCreateOrReplace = {"roleName": "room.member"} + member = client.create_or_replace_room_member("sample-room", "sample-user", resource) + print(member.user_id, member.role_name) + + +def plain_dictionary(client: WebPubSubChatServiceClient) -> None: + member = client.create_or_replace_room_member("sample-room", "sample-user", {"roleName": "room.member"}) + print(member.user_id, member.role_name) + + +def replace_topic_with_typed_dictionary(client: WebPubSubChatServiceClient) -> None: + """Replace an existing topic, retaining an explicit lifecycle state.""" + topic: types.ChatTopicCreateOrReplace = {"title": "Sample topic", "state": models.ChatTopicState.NORMAL} + result = client.create_or_replace_topic("sample-room", "sample-topic", topic=topic) + print(result.id, result.state) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py index 15240242232a..7c688baa8c10 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions.py @@ -3,17 +3,19 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- import os -import uuid - -from azure.core.exceptions import HttpResponseError from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient -from azure.messaging.webpubsubchatservice.models import ( - ChatPermission, - ChatRole, - ChatRoom, - ChatRoomMember, - HumanChatUser, -) + + +def create_role(client): + # [START create_role] + from azure.messaging.webpubsubchatservice.models import ChatPermission, ChatRoleCreateOrReplace + + role = client.create_or_replace_role( + "user.example", + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), + ) + print(role.name, role.permissions) + # [END create_role] def main(): @@ -22,44 +24,11 @@ def main(): print("Set WPS_CHAT_CONNECTION_STRING to run this sample.") return - suffix = uuid.uuid4().hex[:8] - user_role = f"user.sample_{suffix}" - room_role = f"room.sample_{suffix}" - user_id = f"sample-user-{suffix}" - room_id = f"sample-room-{suffix}" with WebPubSubChatServiceClient.from_connection_string( connection_string, os.environ.get("WPS_CHAT_HUB", "test_hub"), ) as client: - try: - client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) - client.create_or_replace_role(room_role, ChatRole(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE])) - client.create_or_replace_user( - user_id, - HumanChatUser(nickname="Sample User", role_name=user_role), - ) - client.create_or_replace_room(room_id, ChatRoom(title="Sample Room")) - client.create_or_replace_room_member( - room_id, - user_id, - ChatRoomMember(role_name=room_role), - ) - for member in client.list_room_members(room_id): - print(member.user_id, member.role_name) - except HttpResponseError as error: - print(f"Chat service request failed: {error}") - raise - finally: - for action, args in ( - (client.delete_room, (room_id,)), - (client.delete_user, (user_id,)), - (client.delete_role, (user_role,)), - (client.delete_role, (room_role,)), - ): - try: - action(*args) - except HttpResponseError: - pass + create_role(client) if __name__ == "__main__": diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py index 3bedcefe5599..b846520977c7 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_roles_and_permissions_async.py @@ -4,17 +4,19 @@ # -------------------------------------------------------------------------- import asyncio import os -import uuid - -from azure.core.exceptions import HttpResponseError from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient -from azure.messaging.webpubsubchatservice.models import ( - ChatPermission, - ChatRole, - ChatRoom, - ChatRoomMember, - HumanChatUser, -) + + +async def create_role(client): + # [START create_role] + from azure.messaging.webpubsubchatservice.models import ChatPermission, ChatRoleCreateOrReplace + + role = await client.create_or_replace_role( + "user.example", + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), + ) + print(role.name, role.permissions) + # [END create_role] async def main(): @@ -23,42 +25,11 @@ async def main(): print("Set WPS_CHAT_CONNECTION_STRING to run this sample.") return - suffix = uuid.uuid4().hex[:8] - user_role = f"user.async_sample_{suffix}" - room_role = f"room.async_sample_{suffix}" - user_id = f"async-sample-user-{suffix}" - room_id = f"async-sample-room-{suffix}" - client = WebPubSubChatServiceClient.from_connection_string( + async with WebPubSubChatServiceClient.from_connection_string( connection_string, os.environ.get("WPS_CHAT_HUB", "test_hub"), - ) - try: - await client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) - await client.create_or_replace_role(room_role, ChatRole(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE])) - await client.create_or_replace_user( - user_id, - HumanChatUser(nickname="Async Sample User", role_name=user_role), - ) - await client.create_or_replace_room(room_id, ChatRoom(title="Async Sample Room")) - await client.create_or_replace_room_member( - room_id, - user_id, - ChatRoomMember(role_name=room_role), - ) - async for member in client.list_room_members(room_id): - print(member.user_id, member.role_name) - finally: - for action, args in ( - (client.delete_room, (room_id,)), - (client.delete_user, (user_id,)), - (client.delete_role, (user_role,)), - (client.delete_role, (room_role,)), - ): - try: - await action(*args) - except HttpResponseError: - pass - await client.close() + ) as client: + await create_role(client) if __name__ == "__main__": diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_message.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_message.py new file mode 100644 index 000000000000..035ee1afbdde --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_message.py @@ -0,0 +1,17 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Update the content of a sample-specific persisted message.""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatMessageUpdate, MessageContent + + +def sample(client: WebPubSubChatServiceClient) -> None: + message = client.update_message( + "c.sample-room.sample-topic", + "123456789", + ChatMessageUpdate(content=MessageContent(text="Updated sample message")), + ) + print(message.id, message.content) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_topic.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_topic.py new file mode 100644 index 000000000000..7b5a2bf5adaf --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/samples/sample_update_topic.py @@ -0,0 +1,21 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Archive an existing topic only if it has not changed since it was read.""" + +from azure.core import MatchConditions +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.models import ChatTopicState, ChatTopicUpdate + + +def sample(client: WebPubSubChatServiceClient) -> None: + current = client.get_topic("sample-room", "sample-topic") + topic = client.update_topic( + "sample-room", + "sample-topic", + ChatTopicUpdate(state=ChatTopicState.ARCHIVED), + etag=current.etag, + match_condition=MatchConditions.IfNotModified, + ) + print(topic.id, topic.state, topic.etag) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview.py new file mode 100644 index 000000000000..47bb6b0f91fe --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview.py @@ -0,0 +1,278 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +import datetime +import inspect +import json +from unittest.mock import AsyncMock, Mock, patch +from urllib.parse import parse_qs, urlencode, urlparse + +import pytest +from azure.core import MatchConditions +from azure.core.credentials import AzureKeyCredential +from azure.core.exceptions import ResourceModifiedError + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient as AsyncClient +from azure.messaging.webpubsubchatservice.models import ( + ChatMessage, + ChatRole, + ChatRoom, + ChatRoomMember, + ChatTopic, + ChatTopicCreateOrReplace, + ChatTopicState, + ChatTopicUpdate, + ChatUserRoom, + HumanChatUser, +) + +ENDPOINT = "https://example.webpubsub.azure.com" +TOPIC = { + "id": "topic", + "title": "Announcements", + "createdAt": "2026-08-01T00:00:00Z", + "createdBy": "user", + "state": "Normal", + "conversationId": "c.room.topic", + "etag": '"first"', +} +COLLECTIONS = [ + ( + "list_messages", + ("conversation",), + "conversations/conversation/messages", + ChatMessage, + {"createdBy": "user", "content": {}, "createdAt": "2026-08-01T00:00:00Z", "etag": '"message"'}, + "id", + ), + ("list_roles", (), "roles", ChatRole, {"permissions": [], "etag": '"role"'}, "name"), + ( + "list_room_members", + ("room",), + "rooms/room/members", + ChatRoomMember, + {"roleName": "room.member", "etag": '"member"'}, + "userId", + ), + ("list_rooms", (), "rooms", ChatRoom, {"title": "Room", "defaultConversation": "c.room.default"}, "id"), + ("list_users", (), "users", HumanChatUser, {"kind": "Human", "nickname": "User", "roleName": "user.member"}, "id"), + ("list_topics", ("room",), "rooms/room/topics", ChatTopic, TOPIC, "id"), + ("list_rooms_for_user", ("user",), "users/user/rooms", ChatUserRoom, {"title": "Room"}, "id"), +] + + +def _response(payload, status=200): + response = Mock(status_code=status, headers={"content-type": "application/json"}, reason="Test response") + response.json.return_value = payload + response.text.return_value = json.dumps(payload) + return Mock(http_response=response) + + +def _paging_responses(path, fields, identity): + cursor = "first" if path.endswith("/messages") else "next+/=token" + query = urlencode({"api-version": "2026-08-01-preview", "continuationToken": cursor}) + next_link = f"{ENDPOINT}/api/hubs/chat/chat/{path}?{query}" + return [ + _response({"value": [{**fields, identity: "first"}], "nextLink": next_link}), + _response({"value": [{**fields, identity: "second"}]}), + ] + + +def _assert_page_requests(run, path): + first, second = [call.args[0] for call in run.call_args_list] + assert first.method == second.method == "GET" + assert urlparse(first.url).path == f"/api/hubs/chat/chat/{path}" + assert parse_qs(urlparse(first.url).query)["maxpagesize"] == ["1"] + assert "continuationToken" not in parse_qs(urlparse(first.url).query) + assert parse_qs(urlparse(first.url).query)["api-version"] == ["2026-08-01-preview"] + assert urlparse(second.url).path == f"/api/hubs/chat/chat/{path}" + cursor = "first" if path.endswith("/messages") else "next+/=token" + assert parse_qs(urlparse(second.url).query)["continuationToken"] == [cursor] + assert parse_qs(urlparse(second.url).query)["api-version"] == ["2026-08-01-preview"] + + +def _assert_topic_requests(run): + create, get, update, delete = [call.args[0] for call in run.call_args_list] + assert [request.method for request in (create, get, update, delete)] == ["PUT", "GET", "PATCH", "DELETE"] + assert all( + urlparse(request.url).path == "/api/hubs/chat/chat/rooms/room/topics/topic" + for request in (create, get, update, delete) + ) + assert json.loads(create.content) == {"title": "Announcements"} + assert json.loads(update.content) == {"title": "Announcements", "state": "Archived"} + assert update.headers["Content-Type"] == "application/merge-patch+json" + assert update.headers["If-Match"] == '"first"' + assert delete.headers["If-Match"] == '"second"' + + +@pytest.mark.parametrize("client_type", [WebPubSubChatServiceClient, AsyncClient]) +def test_listing_api_surface(client_type): + operations = {name for name in dir(client_type) if name.startswith("list_")} + assert operations == {collection[0] for collection in COLLECTIONS} + for name in operations: + method = getattr(client_type, name) + assert not any("continuation_token" in parameter for parameter in inspect.signature(method).parameters) + assert ":keyword continuation_token" not in method.__doc__ + assert ":paramtype continuation_token" not in method.__doc__ + assert "Message IDs are used as pagination cursors." in " ".join(client_type.list_messages.__doc__.split()) + assert "nextLink" in client_type.list_messages.__doc__ + + +@pytest.mark.parametrize("resume", [False, True]) +@pytest.mark.parametrize("operation,args,path,model,fields,identity", COLLECTIONS) +def test_collection_paging_and_continuation(operation, args, path, model, fields, identity, resume): + with WebPubSubChatServiceClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, "run", side_effect=_paging_responses(path, fields, identity) + ) as run: + options = {"max_page_size": 1} + pages = getattr(client, operation)(*args, **options).by_page() + first = list(next(pages)) + assert len(first) == 1 + assert isinstance(first[0], model) + assert pages.continuation_token + remaining = ( + getattr(client, operation)(*args, **options).by_page(continuation_token=pages.continuation_token) + if resume + else pages + ) + second = list(next(remaining)) + assert isinstance(second[0], model) + assert [item[identity] for item in first + second] == ["first", "second"] + assert not list(remaining) + _assert_page_requests(run, path) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("resume", [False, True]) +@pytest.mark.parametrize("operation,args,path,model,fields,identity", COLLECTIONS) +async def test_async_collection_paging_and_continuation(operation, args, path, model, fields, identity, resume): + async with AsyncClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, + "run", + new_callable=AsyncMock, + side_effect=_paging_responses(path, fields, identity), + ) as run: + options = {"max_page_size": 1} + pages = getattr(client, operation)(*args, **options).by_page() + first = [item async for item in await anext(pages)] + assert len(first) == 1 + assert isinstance(first[0], model) + assert pages.continuation_token + remaining = ( + getattr(client, operation)(*args, **options).by_page(continuation_token=pages.continuation_token) + if resume + else pages + ) + second = [item async for item in await anext(remaining)] + assert isinstance(second[0], model) + assert [item[identity] for item in first + second] == ["first", "second"] + assert [page async for page in remaining] == [] + _assert_page_requests(run, path) + + +def test_topic_serialization_lifecycle_and_etag(): + archived = {**TOPIC, "state": "Archived", "etag": '"second"'} + with WebPubSubChatServiceClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, + "run", + side_effect=[_response(TOPIC, 201), _response(TOPIC), _response(archived), _response({}, 204)], + ) as run: + topic = client.create_or_replace_topic("room", "topic", ChatTopicCreateOrReplace(title=TOPIC["title"])) + assert topic.created_at == datetime.datetime(2026, 8, 1, tzinfo=datetime.timezone.utc) + fetched = client.get_topic("room", "topic") + assert fetched.state == ChatTopicState.NORMAL + updated = client.update_topic( + "room", + "topic", + ChatTopicUpdate(title=topic.title, state=ChatTopicState.ARCHIVED), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert updated.state == ChatTopicState.ARCHIVED + assert updated.conversation_id == topic.conversation_id + assert ( + client.delete_topic("room", "topic", etag=updated.etag, match_condition=MatchConditions.IfNotModified) + is None + ) + _assert_topic_requests(run) + + +@pytest.mark.asyncio +async def test_async_topic_serialization_lifecycle_and_etag(): + archived = {**TOPIC, "state": "Archived", "etag": '"second"'} + async with AsyncClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, + "run", + new_callable=AsyncMock, + side_effect=[_response(TOPIC, 201), _response(TOPIC), _response(archived), _response({}, 204)], + ) as run: + topic = await client.create_or_replace_topic( + "room", "topic", ChatTopicCreateOrReplace(title=TOPIC["title"]) + ) + assert topic.created_at == datetime.datetime(2026, 8, 1, tzinfo=datetime.timezone.utc) + fetched = await client.get_topic("room", "topic") + assert fetched.state == ChatTopicState.NORMAL + updated = await client.update_topic( + "room", + "topic", + ChatTopicUpdate(title=topic.title, state=ChatTopicState.ARCHIVED), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert updated.state == ChatTopicState.ARCHIVED + assert updated.conversation_id == topic.conversation_id + assert ( + await client.delete_topic( + "room", "topic", etag=updated.etag, match_condition=MatchConditions.IfNotModified + ) + is None + ) + _assert_topic_requests(run) + + +def test_topic_stale_etag_surfaces_service_error(): + with WebPubSubChatServiceClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, + "run", + return_value=_response({"error": {"code": "PreconditionFailed", "message": "Stale ETag"}}, 412), + ): + with pytest.raises(ResourceModifiedError) as error: + client.update_topic( + "room", + "topic", + ChatTopicUpdate(title="Title", state=ChatTopicState.ARCHIVED), + etag='"stale"', + match_condition=MatchConditions.IfNotModified, + ) + assert error.value.status_code == 412 + + +@pytest.mark.asyncio +async def test_async_topic_stale_etag_surfaces_service_error(): + async with AsyncClient(ENDPOINT, "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, + "run", + new_callable=AsyncMock, + return_value=_response({"error": {"code": "PreconditionFailed", "message": "Stale ETag"}}, 412), + ): + with pytest.raises(ResourceModifiedError) as error: + await client.update_topic( + "room", + "topic", + ChatTopicUpdate(title="Title", state=ChatTopicState.ARCHIVED), + etag='"stale"', + match_condition=MatchConditions.IfNotModified, + ) + assert error.value.status_code == 412 + + +def test_topic_unknown_state_remains_extensible(): + assert ChatTopic({**TOPIC, "state": "FutureState"}).state == "FutureState" diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live.py new file mode 100644 index 000000000000..7eaa8ecd712b --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live.py @@ -0,0 +1,128 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +from devtools_testutils import recorded_by_proxy +import pytest +from testcase import WebPubSubChatPreparer, WebPubSubChatTest + +from azure.core import MatchConditions +from azure.core.exceptions import HttpResponseError +from azure.messaging.webpubsubchatservice.models import ( + ChatPermission, + ChatRoleCreateOrReplace, + ChatRoomCreateOrReplace, + ChatRoomMemberCreateOrReplace, + ChatTopicCreateOrReplace, + ChatTopicUpdate, + ChatTopicState, + HumanChatUser, + HumanChatUserCreateOrReplace, +) + + +def _assert_resumable_pages(paged): + pages = paged.by_page() + first = list(next(pages)) + assert len(first) == 1 + assert pages.continuation_token + resumed = paged.by_page(pages.continuation_token) + second = list(next(resumed)) + assert len(second) == 1 + assert first[0].id != second[0].id + + +class TestWebPubSubChatTopicsLive(WebPubSubChatTest): + @WebPubSubChatPreparer() + @recorded_by_proxy + def test_topic_lifecycle_and_paging(self, wps_chat_endpoint): + client = self.create_client(wps_chat_endpoint) + room_id = "python-e2e-topics" + try: + room = client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Topic E2E")) + assert all(topic.conversation_id == room.default_conversation for topic in client.list_topics(room_id)) + for topic_id in ("first", "second"): + topic = client.create_or_replace_topic(room_id, topic_id, ChatTopicCreateOrReplace(title=topic_id)) + assert topic.id == topic_id + assert topic.title == topic_id + assert topic.state == ChatTopicState.NORMAL + assert topic.created_at + assert topic.created_by + assert topic.etag + conversation = client.get_conversation(topic.conversation_id) + assert conversation.id == topic.conversation_id + assert conversation.room_id == room_id + assert not list(client.list_messages(topic.conversation_id)) + + assert {topic.id for topic in client.list_topics(room_id, max_page_size=1)}.issuperset({"first", "second"}) + _assert_resumable_pages(client.list_topics(room_id, max_page_size=1)) + fetched = client.get_topic(room_id, "first") + archived = client.update_topic( + room_id, + "first", + ChatTopicUpdate(title="Archived topic", state=ChatTopicState.ARCHIVED), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert archived.title == "Archived topic" + assert archived.state == ChatTopicState.ARCHIVED + assert archived.conversation_id == fetched.conversation_id + assert archived.etag != fetched.etag + assert client.get_topic(room_id, "first").state == ChatTopicState.ARCHIVED + with pytest.raises(HttpResponseError) as error: + client.update_topic( + room_id, + "first", + ChatTopicUpdate(title="Stale update", state=ChatTopicState.NORMAL), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert error.value.status_code == 412 + client.delete_topic(room_id, "first", etag=archived.etag, match_condition=MatchConditions.IfNotModified) + finally: + self.cleanup(client.delete_room, room_id) + client.close() + + @WebPubSubChatPreparer() + @recorded_by_proxy + def test_room_user_and_user_room_collections(self, wps_chat_endpoint): + client = self.create_client(wps_chat_endpoint) + room_ids = ["python-e2e-collection-1", "python-e2e-collection-2"] + user_ids = ["python-e2e-collection-user-1", "python-e2e-collection-user-2"] + user_role = "user.python_e2e_collections" + room_role = "room.python_e2e_collections" + try: + client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) + client.create_or_replace_role( + room_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE]) + ) + for user_id in user_ids: + client.create_or_replace_user( + user_id, HumanChatUserCreateOrReplace(nickname=user_id, role_name=user_role) + ) + assert not list(client.list_rooms_for_user(user_ids[0])) + for room_id in room_ids: + client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title=room_id)) + client.create_or_replace_room_member( + room_id, user_ids[0], ChatRoomMemberCreateOrReplace(role_name=room_role) + ) + assert {room.id for room in client.list_rooms(max_page_size=1)}.issuperset(room_ids) + users = list(client.list_users(max_page_size=1)) + assert {user.id for user in users}.issuperset(user_ids) + assert all(isinstance(user, HumanChatUser) for user in users if user.id in user_ids) + assert {room.id for room in client.list_rooms_for_user(user_ids[0])} == set(room_ids) + _assert_resumable_pages(client.list_rooms(max_page_size=1)) + _assert_resumable_pages(client.list_users(max_page_size=1)) + _assert_resumable_pages(client.list_rooms_for_user(user_ids[0], max_page_size=1)) + client.delete_room_member(room_ids[0], user_ids[0]) + assert {room.id for room in client.list_rooms_for_user(user_ids[0])} == {room_ids[1]} + finally: + for room_id in room_ids: + self.cleanup(client.delete_room, room_id) + for user_id in user_ids: + self.cleanup(client.delete_user, user_id) + self.cleanup(client.delete_role, user_role) + self.cleanup(client.delete_role, room_role) + client.close() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live_async.py new file mode 100644 index 000000000000..f1c896dd32e1 --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_2026_08_01_preview_live_async.py @@ -0,0 +1,143 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +import pytest +from devtools_testutils.aio import recorded_by_proxy_async +from testcase import WebPubSubChatPreparer, WebPubSubChatTest + +from azure.core import MatchConditions +from azure.core.exceptions import HttpResponseError +from azure.messaging.webpubsubchatservice.models import ( + ChatPermission, + ChatRoleCreateOrReplace, + ChatRoomCreateOrReplace, + ChatRoomMemberCreateOrReplace, + ChatTopicCreateOrReplace, + ChatTopicUpdate, + ChatTopicState, + HumanChatUser, + HumanChatUserCreateOrReplace, +) + + +async def _collect(paged): + return [item async for item in paged] + + +async def _assert_resumable_pages(paged): + pages = paged.by_page() + first = await _collect(await anext(pages)) + assert len(first) == 1 + assert pages.continuation_token + resumed = paged.by_page(pages.continuation_token) + second = await _collect(await anext(resumed)) + assert len(second) == 1 + assert first[0].id != second[0].id + + +class TestWebPubSubChatTopicsLiveAsync(WebPubSubChatTest): + @WebPubSubChatPreparer() + @recorded_by_proxy_async + @pytest.mark.asyncio + async def test_async_topic_lifecycle_and_paging(self, wps_chat_endpoint): + client = self.create_async_client(wps_chat_endpoint) + room_id = "python-async-e2e-topics" + try: + room = await client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Async Topic E2E")) + assert all( + topic.conversation_id == room.default_conversation + for topic in await _collect(client.list_topics(room_id)) + ) + for topic_id in ("first", "second"): + topic = await client.create_or_replace_topic( + room_id, topic_id, ChatTopicCreateOrReplace(title=topic_id) + ) + assert topic.id == topic_id + assert topic.title == topic_id + assert topic.state == ChatTopicState.NORMAL + assert topic.created_at + assert topic.created_by + assert topic.etag + conversation = await client.get_conversation(topic.conversation_id) + assert conversation.id == topic.conversation_id + assert conversation.room_id == room_id + assert await _collect(client.list_messages(topic.conversation_id)) == [] + + assert {topic.id async for topic in client.list_topics(room_id, max_page_size=1)}.issuperset( + {"first", "second"} + ) + await _assert_resumable_pages(client.list_topics(room_id, max_page_size=1)) + fetched = await client.get_topic(room_id, "first") + archived = await client.update_topic( + room_id, + "first", + ChatTopicUpdate(title="Archived topic", state=ChatTopicState.ARCHIVED), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert archived.title == "Archived topic" + assert archived.state == ChatTopicState.ARCHIVED + assert archived.conversation_id == fetched.conversation_id + assert archived.etag != fetched.etag + assert (await client.get_topic(room_id, "first")).state == ChatTopicState.ARCHIVED + with pytest.raises(HttpResponseError) as error: + await client.update_topic( + room_id, + "first", + ChatTopicUpdate(title="Stale update", state=ChatTopicState.NORMAL), + etag=fetched.etag, + match_condition=MatchConditions.IfNotModified, + ) + assert error.value.status_code == 412 + await client.delete_topic( + room_id, "first", etag=archived.etag, match_condition=MatchConditions.IfNotModified + ) + finally: + await self.cleanup_async(client.delete_room, room_id) + await client.close() + + @WebPubSubChatPreparer() + @recorded_by_proxy_async + @pytest.mark.asyncio + async def test_async_room_user_and_user_room_collections(self, wps_chat_endpoint): + client = self.create_async_client(wps_chat_endpoint) + room_ids = ["python-async-e2e-collection-1", "python-async-e2e-collection-2"] + user_ids = ["python-async-e2e-collection-user-1", "python-async-e2e-collection-user-2"] + user_role = "user.python_async_e2e_collections" + room_role = "room.python_async_e2e_collections" + try: + await client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) + await client.create_or_replace_role( + room_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE]) + ) + for user_id in user_ids: + await client.create_or_replace_user( + user_id, HumanChatUserCreateOrReplace(nickname=user_id, role_name=user_role) + ) + assert await _collect(client.list_rooms_for_user(user_ids[0])) == [] + for room_id in room_ids: + await client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title=room_id)) + await client.create_or_replace_room_member( + room_id, user_ids[0], ChatRoomMemberCreateOrReplace(role_name=room_role) + ) + assert {room.id async for room in client.list_rooms(max_page_size=1)}.issuperset(room_ids) + users = await _collect(client.list_users(max_page_size=1)) + assert {user.id for user in users}.issuperset(user_ids) + assert all(isinstance(user, HumanChatUser) for user in users if user.id in user_ids) + assert {room.id async for room in client.list_rooms_for_user(user_ids[0])} == set(room_ids) + await _assert_resumable_pages(client.list_rooms(max_page_size=1)) + await _assert_resumable_pages(client.list_users(max_page_size=1)) + await _assert_resumable_pages(client.list_rooms_for_user(user_ids[0], max_page_size=1)) + await client.delete_room_member(room_ids[0], user_ids[0]) + assert {room.id async for room in client.list_rooms_for_user(user_ids[0])} == {room_ids[1]} + finally: + for room_id in room_ids: + await self.cleanup_async(client.delete_room, room_id) + for user_id in user_ids: + await self.cleanup_async(client.delete_user, user_id) + await self.cleanup_async(client.delete_role, user_role) + await self.cleanup_async(client.delete_role, room_role) + await client.close() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client.py index 37fd350352d3..085ee43fe2b8 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- import runpy +import subprocess +import sys import time from pathlib import Path from unittest.mock import Mock, patch @@ -30,7 +32,14 @@ class RequestCaptured(Exception): @pytest.mark.parametrize( "operation_name,arguments", - [("list_roles", ()), ("list_messages", ("conversation",)), ("list_room_members", ("room",))], + [ + ("list_roles", ()), + ("list_messages", ("conversation",)), + ("list_room_members", ("room",)), + ("list_rooms", ()), + ("list_users", ()), + ("list_topics", ("room",)), + ], ) def test_max_page_size_uses_original_wire_name(operation_name, arguments): def capture(pipeline_request): @@ -45,7 +54,7 @@ def capture(pipeline_request): class FakeTokenCredential: - def get_token(self, *scopes, **kwargs): + def get_token(self, *_scopes, **_kwargs): return AccessToken("entra-token", int(time.time()) + 3600) @@ -113,7 +122,7 @@ def test_key_credential_request_uses_full_uri_audience_and_sixty_second_token(): claims = jwt.decode(token, ACCESS_KEY, algorithms=["HS256"], audience=request.url) assert claims["aud"] == request.url - assert "api-version=2026-02-01-preview" in request.url + assert "api-version=2026-08-01-preview" in request.url assert 0 < claims["exp"] - int(time.time()) <= 60 finally: client.close() @@ -264,6 +273,32 @@ def test_client_access_sample_does_not_log_token(monkeypatch, capsys): assert captured.err == "" +@pytest.mark.parametrize( + "sample_name,variable", + [ + ("sample_errors.py", "WPS_CHAT_CONNECTION_STRING"), + ("sample_logging.py", "WPS_CHAT_ENDPOINT"), + ("sample_roles_and_permissions.py", "WPS_CHAT_CONNECTION_STRING"), + ("sample_roles_and_permissions_async.py", "WPS_CHAT_CONNECTION_STRING"), + ], +) +def test_sample_without_configuration_exits_successfully(sample_name, variable, monkeypatch): + monkeypatch.delenv(variable, raising=False) + sample_path = Path(__file__).resolve().parents[1] / "samples" / sample_name + + result = subprocess.run( + [sys.executable, str(sample_path)], + capture_output=True, + text=True, + check=False, + timeout=30, + ) + + assert result.returncode == 0, result.stderr + assert result.stdout == f"Set {variable} to run this sample.\n" + assert result.stderr == "" + + def test_builtin_roles_and_generated_permissions(): assert BuiltInChatRoles.USER_NORMAL == "user.normal" assert BuiltInChatRoles.ROOM_MEMBER == "room.member" diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client_async.py index e6a00a7e1cc9..32f156c93119 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client_async.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_client_async.py @@ -25,7 +25,7 @@ class RequestCaptured(Exception): class FakeAsyncTokenCredential: - async def get_token(self, *scopes, **kwargs): + async def get_token(self, *_scopes, **_kwargs): return AccessToken("entra-token", int(time.time()) + 3600) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live.py index 44eb9d3ded9c..da1457a6bf6e 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live.py @@ -4,25 +4,24 @@ # -------------------------------------------------------------------------- import pytest from devtools_testutils import recorded_by_proxy +from testcase import ( + WebPubSubChatAccessPreparer, + WebPubSubChatPreparer, + WebPubSubChatTest, +) from azure.core import MatchConditions from azure.core.exceptions import HttpResponseError from azure.messaging.webpubsubchatservice.models import ( - ChatMessage, + ChatMessageUpdate, ChatPermission, - ChatRole, - ChatRoom, - ChatRoomMember, - HumanChatUser, + ChatRoleCreateOrReplace, + ChatRoomCreateOrReplace, + ChatRoomMemberCreateOrReplace, + HumanChatUserCreateOrReplace, MessageContent, ) -from testcase import ( - WebPubSubChatAccessPreparer, - WebPubSubChatPreparer, - WebPubSubChatTest, -) - class TestWebPubSubChatLive(WebPubSubChatTest): @WebPubSubChatPreparer() @@ -34,7 +33,7 @@ def test_role_lifecycle_and_paging(self, wps_chat_endpoint): for role_name in role_names: role = client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), ) assert role.name == role_name assert client.get_role(role_name).permissions == [ChatPermission.USER_CREATE_ROOM] @@ -60,7 +59,6 @@ def test_role_lifecycle_and_paging(self, wps_chat_endpoint): @recorded_by_proxy def test_list_roles_using_connection_string( self, - wps_chat_endpoint, wps_chat_connection_string, wps_chat_disable_local_auth, ): @@ -82,7 +80,7 @@ def test_create_role_only_when_missing(self, wps_chat_endpoint): try: created = client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), match_condition=MatchConditions.IfMissing, ) assert created.name == role_name @@ -90,7 +88,7 @@ def test_create_role_only_when_missing(self, wps_chat_endpoint): with pytest.raises(HttpResponseError) as error: client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_FETCH_ALL_ROOMS]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_FETCH_ALL_ROOMS]), match_condition=MatchConditions.IfMissing, ) assert error.value.status_code == 412 @@ -106,11 +104,11 @@ def test_rejects_stale_role_etag(self, wps_chat_endpoint): try: created = client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), ) replaced = client.create_or_replace_role( role_name, - ChatRole( + ChatRoleCreateOrReplace( permissions=[ ChatPermission.USER_CREATE_ROOM, ChatPermission.USER_FETCH_ALL_ROOMS, @@ -124,7 +122,7 @@ def test_rejects_stale_role_etag(self, wps_chat_endpoint): with pytest.raises(HttpResponseError) as error: client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), etag=created.etag, match_condition=MatchConditions.IfNotModified, ) @@ -139,7 +137,7 @@ def test_room_conversation_and_empty_pages(self, wps_chat_endpoint): client = self.create_client(wps_chat_endpoint) room_id = "python-e2e-empty-room" try: - room = client.create_or_replace_room(room_id, ChatRoom(title="Python E2E Room")) + room = client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Python E2E Room")) assert room.id == room_id fetched_room = client.get_room(room_id) assert fetched_room.id == room_id @@ -147,9 +145,9 @@ def test_room_conversation_and_empty_pages(self, wps_chat_endpoint): conversation = client.get_conversation(room.default_conversation) assert conversation.id == room.default_conversation - assert conversation.parent_room == room_id - assert list(client.list_messages(room.default_conversation)) == [] - assert list(client.list_room_members(room_id)) == [] + assert conversation.room_id == room_id + assert not list(client.list_messages(room.default_conversation)) + assert not list(client.list_room_members(room_id)) finally: self.cleanup(client.delete_room, room_id) client.close() @@ -163,11 +161,15 @@ def test_user_and_room_member_lifecycle(self, wps_chat_endpoint): user_id = "python-e2e-user" room_id = "python-e2e-member-room" try: - client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) - client.create_or_replace_role(room_role, ChatRole(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE])) + client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) + client.create_or_replace_role( + room_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE]) + ) user = client.create_or_replace_user( user_id, - HumanChatUser(nickname="Python User", role_name=user_role), + HumanChatUserCreateOrReplace(nickname="Python User", role_name=user_role), ) assert user.id == user_id assert user.kind == "Human" @@ -175,11 +177,11 @@ def test_user_and_room_member_lifecycle(self, wps_chat_endpoint): assert fetched_user.id == user_id assert fetched_user.nickname == "Python User" - client.create_or_replace_room(room_id, ChatRoom(title="Member Room")) + client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Member Room")) member = client.create_or_replace_room_member( room_id, user_id, - ChatRoomMember(role_name=room_role), + ChatRoomMemberCreateOrReplace(role_name=room_role), ) assert member.user_id == user_id assert member.role_name == room_role @@ -206,10 +208,12 @@ def test_message_lifecycle(self, wps_chat_endpoint): updated_text = "Python E2E updated message" binary_content = bytes([0, 1, 2, 254, 255]) try: - client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) + client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) client.create_or_replace_role( room_role, - ChatRole( + ChatRoleCreateOrReplace( permissions=[ ChatPermission.ROOM_PUBLISH_MESSAGE, ChatPermission.ROOM_HISTORY, @@ -218,10 +222,10 @@ def test_message_lifecycle(self, wps_chat_endpoint): ) client.create_or_replace_user( user_id, - HumanChatUser(nickname="Python Message User", role_name=user_role), + HumanChatUserCreateOrReplace(nickname="Python Message User", role_name=user_role), ) - room = client.create_or_replace_room(room_id, ChatRoom(title="Python Message Room")) - client.create_or_replace_room_member(room_id, user_id, ChatRoomMember(role_name=room_role)) + room = client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Python Message Room")) + client.create_or_replace_room_member(room_id, user_id, ChatRoomMemberCreateOrReplace(role_name=room_role)) self.seed_chat_message(client, user_id, room.default_conversation, message_text) message = next( @@ -232,13 +236,13 @@ def test_message_lifecycle(self, wps_chat_endpoint): updated = client.update_message( room.default_conversation, message.id, - ChatMessage(created_by=user_id, content=MessageContent(text=updated_text)), + ChatMessageUpdate(created_by=user_id, content=MessageContent(text=updated_text)), ) assert updated.content.text == updated_text binary_updated = client.update_message( room.default_conversation, message.id, - ChatMessage( + ChatMessageUpdate( created_by=user_id, content=MessageContent(binary=binary_content), ), @@ -263,15 +267,20 @@ def test_client_access_token( ): token_client = self.create_client(wps_chat_endpoint) key_client = None + token_user_id = "python-e2e-token-access-user" + key_user_id = "python-e2e-key-access-user" try: - token_access = token_client.get_client_access_token(user_id="python-e2e-token-access-user") + token_access = token_client.get_client_access_token(user_id=token_user_id) self.assert_client_access(token_access, wps_chat_endpoint) if wps_chat_disable_local_auth.lower() != "true": key_client = self.create_key_client(wps_chat_connection_string) - key_access = key_client.get_client_access_token(user_id="python-e2e-key-access-user") + key_access = key_client.get_client_access_token(user_id=key_user_id) self.assert_client_access(key_access, wps_chat_endpoint) finally: + self.cleanup(token_client.delete_user, token_user_id) + if key_client: + self.cleanup(token_client.delete_user, key_user_id) token_client.close() if key_client: key_client.close() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live_async.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live_async.py index 157929fe33a8..2479ecb70afd 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live_async.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_live_async.py @@ -4,25 +4,24 @@ # -------------------------------------------------------------------------- import pytest from devtools_testutils.aio import recorded_by_proxy_async +from testcase import ( + WebPubSubChatAccessPreparer, + WebPubSubChatPreparer, + WebPubSubChatTest, +) from azure.core import MatchConditions from azure.core.exceptions import HttpResponseError from azure.messaging.webpubsubchatservice.models import ( - ChatMessage, + ChatMessageUpdate, ChatPermission, - ChatRole, - ChatRoom, - ChatRoomMember, - HumanChatUser, + ChatRoleCreateOrReplace, + ChatRoomCreateOrReplace, + ChatRoomMemberCreateOrReplace, + HumanChatUserCreateOrReplace, MessageContent, ) -from testcase import ( - WebPubSubChatAccessPreparer, - WebPubSubChatPreparer, - WebPubSubChatTest, -) - async def _collect(paged): return [item async for item in paged] @@ -39,7 +38,7 @@ async def test_async_role_lifecycle_and_paging(self, wps_chat_endpoint): for role_name in role_names: role = await client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), ) assert role.name == role_name assert (await client.get_role(role_name)).permissions == [ChatPermission.USER_CREATE_ROOM] @@ -66,7 +65,6 @@ async def test_async_role_lifecycle_and_paging(self, wps_chat_endpoint): @pytest.mark.asyncio async def test_async_list_roles_using_connection_string( self, - wps_chat_endpoint, wps_chat_connection_string, wps_chat_disable_local_auth, ): @@ -89,7 +87,7 @@ async def test_async_create_role_only_when_missing(self, wps_chat_endpoint): try: created = await client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), match_condition=MatchConditions.IfMissing, ) assert created.name == role_name @@ -97,7 +95,7 @@ async def test_async_create_role_only_when_missing(self, wps_chat_endpoint): with pytest.raises(HttpResponseError) as error: await client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_FETCH_ALL_ROOMS]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_FETCH_ALL_ROOMS]), match_condition=MatchConditions.IfMissing, ) assert error.value.status_code == 412 @@ -114,11 +112,11 @@ async def test_async_rejects_stale_role_etag(self, wps_chat_endpoint): try: created = await client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), ) replaced = await client.create_or_replace_role( role_name, - ChatRole( + ChatRoleCreateOrReplace( permissions=[ ChatPermission.USER_CREATE_ROOM, ChatPermission.USER_FETCH_ALL_ROOMS, @@ -132,7 +130,7 @@ async def test_async_rejects_stale_role_etag(self, wps_chat_endpoint): with pytest.raises(HttpResponseError) as error: await client.create_or_replace_role( role_name, - ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM]), + ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]), etag=created.etag, match_condition=MatchConditions.IfNotModified, ) @@ -148,7 +146,7 @@ async def test_async_room_conversation_and_empty_pages(self, wps_chat_endpoint): client = self.create_async_client(wps_chat_endpoint) room_id = "python-async-e2e-empty-room" try: - room = await client.create_or_replace_room(room_id, ChatRoom(title="Async Python E2E Room")) + room = await client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Async Python E2E Room")) assert room.id == room_id fetched_room = await client.get_room(room_id) assert fetched_room.id == room_id @@ -156,7 +154,7 @@ async def test_async_room_conversation_and_empty_pages(self, wps_chat_endpoint): conversation = await client.get_conversation(room.default_conversation) assert conversation.id == room.default_conversation - assert conversation.parent_room == room_id + assert conversation.room_id == room_id assert await _collect(client.list_messages(room.default_conversation)) == [] assert await _collect(client.list_room_members(room_id)) == [] finally: @@ -173,23 +171,27 @@ async def test_async_user_and_room_member_lifecycle(self, wps_chat_endpoint): user_id = "python-async-e2e-user" room_id = "python-async-e2e-member-room" try: - await client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) - await client.create_or_replace_role(room_role, ChatRole(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE])) + await client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) + await client.create_or_replace_role( + room_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.ROOM_PUBLISH_MESSAGE]) + ) user = await client.create_or_replace_user( user_id, - HumanChatUser(nickname="Async Python User", role_name=user_role), + HumanChatUserCreateOrReplace(nickname="Async Python User", role_name=user_role), ) assert user.id == user_id assert user.kind == "Human" fetched_user = await client.get_user(user_id) assert fetched_user.id == user_id assert fetched_user.nickname == "Async Python User" - await client.create_or_replace_room(room_id, ChatRoom(title="Async Member Room")) + await client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Async Member Room")) member = await client.create_or_replace_room_member( room_id, user_id, - ChatRoomMember(role_name=room_role), + ChatRoomMemberCreateOrReplace(role_name=room_role), ) assert member.user_id == user_id assert member.role_name == room_role @@ -218,10 +220,12 @@ async def test_async_message_lifecycle(self, wps_chat_endpoint): updated_text = "Python async E2E updated message" binary_content = bytes([0, 1, 2, 254, 255]) try: - await client.create_or_replace_role(user_role, ChatRole(permissions=[ChatPermission.USER_CREATE_ROOM])) + await client.create_or_replace_role( + user_role, ChatRoleCreateOrReplace(permissions=[ChatPermission.USER_CREATE_ROOM]) + ) await client.create_or_replace_role( room_role, - ChatRole( + ChatRoleCreateOrReplace( permissions=[ ChatPermission.ROOM_PUBLISH_MESSAGE, ChatPermission.ROOM_HISTORY, @@ -230,10 +234,12 @@ async def test_async_message_lifecycle(self, wps_chat_endpoint): ) await client.create_or_replace_user( user_id, - HumanChatUser(nickname="Async Message User", role_name=user_role), + HumanChatUserCreateOrReplace(nickname="Async Message User", role_name=user_role), + ) + room = await client.create_or_replace_room(room_id, ChatRoomCreateOrReplace(title="Async Message Room")) + await client.create_or_replace_room_member( + room_id, user_id, ChatRoomMemberCreateOrReplace(role_name=room_role) ) - room = await client.create_or_replace_room(room_id, ChatRoom(title="Async Message Room")) - await client.create_or_replace_room_member(room_id, user_id, ChatRoomMember(role_name=room_role)) await self.seed_chat_message_async(client, user_id, room.default_conversation, message_text) messages = await _collect(client.list_messages(room.default_conversation)) @@ -243,13 +249,13 @@ async def test_async_message_lifecycle(self, wps_chat_endpoint): updated = await client.update_message( room.default_conversation, message.id, - ChatMessage(created_by=user_id, content=MessageContent(text=updated_text)), + ChatMessageUpdate(created_by=user_id, content=MessageContent(text=updated_text)), ) assert updated.content.text == updated_text binary_updated = await client.update_message( room.default_conversation, message.id, - ChatMessage( + ChatMessageUpdate( created_by=user_id, content=MessageContent(binary=binary_content), ), @@ -277,15 +283,20 @@ async def test_async_client_access_token( ): token_client = self.create_async_client(wps_chat_endpoint) key_client = None + token_user_id = "python-async-e2e-token-access-user" + key_user_id = "python-async-e2e-key-access-user" try: - token_access = await token_client.get_client_access_token(user_id="python-async-e2e-token-access-user") + token_access = await token_client.get_client_access_token(user_id=token_user_id) await self.assert_client_access_async(token_access, wps_chat_endpoint) if wps_chat_disable_local_auth.lower() != "true": key_client = self.create_async_key_client(wps_chat_connection_string) - key_access = await key_client.get_client_access_token(user_id="python-async-e2e-key-access-user") + key_access = await key_client.get_client_access_token(user_id=key_user_id) await self.assert_client_access_async(key_access, wps_chat_endpoint) finally: + await self.cleanup_async(token_client.delete_user, token_user_id) + if key_client: + await self.cleanup_async(token_client.delete_user, key_user_id) await token_client.close() if key_client: await key_client.close() diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_operation_samples.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_operation_samples.py new file mode 100644 index 000000000000..2c030a0b6b2f --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_operation_samples.py @@ -0,0 +1,202 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +import ast +import importlib.util +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import Mock + +import pytest + +from azure.core import MatchConditions +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient, models +from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient as AsyncClient + +PACKAGE = Path(__file__).resolve().parents[1] +SAMPLES = PACKAGE / "samples" +IDENTIFIERS = { + "role": ("room.sample",), + "room": ("sample-room",), + "room_member": ("sample-room", "sample-user"), + "user": ("sample-user",), + "topic": ("sample-room", "sample-topic"), + "message": ("c.sample-room.sample-topic", "123456789"), +} +REQUESTS = { + "create_or_replace_role": ( + models.ChatRoleCreateOrReplace, + {"permissions": ["room.history", "room.publish_message"]}, + ), + "create_or_replace_room": (models.ChatRoomCreateOrReplace, {"title": "Sample room"}), + "create_or_replace_room_member": (models.ChatRoomMemberCreateOrReplace, {"roleName": "room.member"}), + "create_or_replace_user": ( + models.HumanChatUserCreateOrReplace, + {"kind": "Human", "nickname": "Sample user", "roleName": "user.member"}, + ), + "create_or_replace_topic": (models.ChatTopicCreateOrReplace, {"title": "Sample topic"}), + "update_topic": (models.ChatTopicUpdate, {"state": "Archived"}), + "update_message": (models.ChatMessageUpdate, {"content": {"text": "Updated sample message"}}), +} + + +def _load_sample(name): + path = SAMPLES / f"{name}.py" + spec = importlib.util.spec_from_file_location(name, path) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + return module + + +def _client(): + client = Mock(spec=WebPubSubChatServiceClient) + response = SimpleNamespace( + id="result", + name="room.sample", + permissions=[], + title="Title", + default_conversation="conversation", + user_id="sample-user", + role_name="room.member", + kind="Human", + conversation_id="conversation", + etag='"sample-etag"', + state="Archived", + room_id="sample-room", + created_by="sample-user", + content=None, + ) + for operation in (*REQUESTS, "get_topic"): + getattr(client, operation).return_value = response + return client + + +@pytest.mark.parametrize("operation", REQUESTS) +def test_operation_sample_uses_matching_method_and_models(operation): + module = _load_sample(f"sample_{operation}") + client = _client() + module.sample(client) + expected_calls = ["get_topic", operation] if operation == "update_topic" else [operation] + assert [call[0] for call in client.method_calls] == expected_calls + method = getattr(client, operation) + method.assert_called_once() + args = method.call_args.args + entity = operation.removeprefix("create_or_replace_").removeprefix("update_") + expected_ids = IDENTIFIERS[entity] + assert args[: len(expected_ids)] == expected_ids + request_type, expected_json = REQUESTS[operation] + assert isinstance(args[-1], request_type) + assert args[-1].as_dict() == expected_json + if operation == "update_topic": + client.get_topic.assert_called_once_with(*expected_ids) + assert method.call_args.kwargs == { + "etag": '"sample-etag"', + "match_condition": MatchConditions.IfNotModified, + } + + +def test_operation_sample_coverage(): + body_operations = { + name for name in dir(WebPubSubChatServiceClient) if name.startswith(("create_or_replace_", "update_")) + } + assert set(REQUESTS) == body_operations + assert all((SAMPLES / f"sample_{operation}.py").is_file() for operation in body_operations) + + +@pytest.mark.parametrize("style", ["model_based", "typed_dictionary", "plain_dictionary"]) +def test_request_body_styles_are_equivalent(style): + module = _load_sample("sample_request_body_styles") + client = _client() + getattr(module, style)(client) + client.create_or_replace_room_member.assert_called_once() + room_id, user_id, body = client.create_or_replace_room_member.call_args.args + assert (room_id, user_id) == ("sample-room", "sample-user") + if style == "model_based": + assert isinstance(body, models.ChatRoomMemberCreateOrReplace) + body = body.as_dict() + else: + assert isinstance(body, dict) + assert body == {"roleName": "room.member"} + + +def test_typed_dictionary_topic_put_example_serializes_replace_state(): + module = _load_sample("sample_request_body_styles") + client = _client() + module.replace_topic_with_typed_dictionary(client) + client.create_or_replace_topic.assert_called_once() + request = client.create_or_replace_topic.call_args.kwargs["topic"] + assert request["state"] == models.ChatTopicState.NORMAL + assert models.ChatTopicCreateOrReplace(request).as_dict() == request + + +def test_handwritten_examples_cover_all_service_operations(): + operations = { + name + for name in dir(WebPubSubChatServiceClient) + if name.startswith(("create_or_replace_", "update_", "get_", "list_", "delete_")) + and name != "get_client_access_token" + } + covered = set() + for path in SAMPLES.glob("*.py"): + for node in ast.walk(ast.parse(path.read_text())): + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr in operations: + covered.add(node.func.attr) + assert covered == operations + + +def test_standalone_generated_samples_are_not_delivered(): + assert not list((PACKAGE / "generated_samples").glob("*.py")) + for path in (PACKAGE / "README.md", SAMPLES / "README.md"): + assert "generated_samples/" not in path.read_text() + + +@pytest.mark.parametrize("client_type", [WebPubSubChatServiceClient, AsyncClient]) +@pytest.mark.parametrize("operation", REQUESTS) +def test_retained_method_documentation_describes_resource_body(client_type, operation): + parameter = operation.removeprefix("create_or_replace_").removeprefix("update_") + if parameter == "room_member": + parameter = "member" + doc = getattr(client_type, operation).__doc__ + assert f":param {parameter}:" in doc + assert ":param resource:" not in doc + assert "JSON input template" not in doc + + +@pytest.mark.parametrize( + "operation", + [ + "get_role", + "get_room", + "get_user", + "get_topic", + "get_conversation", + "list_roles", + "list_rooms", + "list_room_members", + "list_users", + "list_topics", + "list_rooms_for_user", + "list_messages", + ], +) +def test_read_operation_samples(operation): + module = _load_sample("sample_read_operations") + client = _client() + method = getattr(client, operation) + method.return_value = [] if operation.startswith("list_") else client.get_topic.return_value + getattr(module, operation)(client) + method.assert_called_once() + assert [call[0] for call in client.method_calls] == [operation] + + +@pytest.mark.parametrize("entity", ["role", "room", "room_member", "user", "topic", "message"]) +def test_delete_operation_samples(entity): + module = _load_sample("sample_delete_operations") + client = _client() + operation = f"delete_{entity}" + getattr(module, operation)(client) + getattr(client, operation).assert_called_once_with(*IDENTIFIERS[entity]) + assert [call[0] for call in client.method_calls] == [operation] diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_request_models.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_request_models.py new file mode 100644 index 000000000000..0263ba955e2f --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/test_request_models.py @@ -0,0 +1,307 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Runtime tests for lifecycle visibility and serialized HTTP requests. + +Static overload and invalid-input checks live in typing/request_models.py. +""" + +# cspell:ignore kwonlyargs +import ast +import inspect +import json +from typing import get_args +from unittest.mock import AsyncMock, Mock, patch + +import pytest +from azure.core import MatchConditions +from azure.core.credentials import AzureKeyCredential + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient as AsyncClient +from azure.messaging.webpubsubchatservice import models, types + +REQUEST_PROJECTIONS = [ + (models.ChatTopicCreateOrReplace, models.ChatTopic, {"create", "update"}), + (models.ChatTopicUpdate, models.ChatTopic, {"update"}), + (models.ChatMessageUpdate, models.ChatMessage, {"update"}), + (models.ChatRoleCreateOrReplace, models.ChatRole, {"create", "update"}), + (models.ChatRoomCreateOrReplace, models.ChatRoom, {"create", "update"}), + (models.ChatRoomMemberCreateOrReplace, models.ChatRoomMember, {"create", "update"}), + (models.ChatUserCreateOrReplace, models.ChatUser, {"create", "update"}), + (models.HumanChatUserCreateOrReplace, models.HumanChatUser, {"create", "update"}), +] + +BODY_PARAMETERS = { + "create_or_replace_role": "role", + "create_or_replace_room": "room", + "create_or_replace_room_member": "member", + "create_or_replace_user": "user", + "create_or_replace_topic": "topic", + "update_topic": "topic", + "update_message": "message", +} + +CASES = [ + ( + "create_or_replace_topic", + ("room", "topic"), + models.ChatTopicCreateOrReplace(title="Title"), + models.ChatTopic, + {"title": "Title"}, + ), + ( + "create_or_replace_topic", + ("room", "topic"), + types.ChatTopicCreateOrReplace(title="Title"), + models.ChatTopic, + {"title": "Title"}, + ), + ( + "create_or_replace_topic", + ("room", "topic"), + models.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED), + models.ChatTopic, + {"title": "Title", "state": "Archived"}, + ), + ( + "create_or_replace_topic", + ("room", "topic"), + types.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED), + models.ChatTopic, + {"title": "Title", "state": "Archived"}, + ), + ( + "update_topic", + ("room", "topic"), + models.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED), + models.ChatTopic, + {"state": "Archived"}, + ), + ( + "update_topic", + ("room", "topic"), + models.ChatTopicUpdate(title="New title"), + models.ChatTopic, + {"title": "New title"}, + ), + ( + "update_message", + ("conversation", "message"), + models.ChatMessageUpdate(created_by="Updated creator"), + models.ChatMessage, + {"createdBy": "Updated creator"}, + ), + ( + "update_topic", + ("room", "topic"), + types.ChatTopicUpdate(title="New title"), + models.ChatTopic, + {"title": "New title"}, + ), + ( + "update_topic", + ("room", "topic"), + types.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED), + models.ChatTopic, + {"state": "Archived"}, + ), + ( + "update_message", + ("conversation", "message"), + types.ChatMessageUpdate(createdBy="Updated creator"), + models.ChatMessage, + {"createdBy": "Updated creator"}, + ), + ( + "update_message", + ("conversation", "message"), + types.ChatMessageUpdate(content={"text": "Updated"}), + models.ChatMessage, + {"content": {"text": "Updated"}}, + ), + ( + "update_message", + ("conversation", "message"), + models.ChatMessageUpdate(content=models.MessageContent(text="Updated")), + models.ChatMessage, + {"content": {"text": "Updated"}}, + ), + ( + "update_message", + ("conversation", "message"), + models.ChatMessageUpdate(content=models.MessageContent(binary=b"\x00\xff")), + models.ChatMessage, + {"content": {"binary": "AP8="}}, + ), + ( + "create_or_replace_role", + ("user.role",), + models.ChatRoleCreateOrReplace(permissions=[models.ChatPermission.USER_CREATE_ROOM]), + models.ChatRole, + {"permissions": ["user.create_room"]}, + ), + ( + "create_or_replace_room", + ("room",), + models.ChatRoomCreateOrReplace(title="Room"), + models.ChatRoom, + {"title": "Room"}, + ), + ( + "create_or_replace_room_member", + ("room", "user"), + models.ChatRoomMemberCreateOrReplace(role_name="room.member"), + models.ChatRoomMember, + {"roleName": "room.member"}, + ), + ( + "create_or_replace_user", + ("user",), + models.HumanChatUserCreateOrReplace(nickname="User", role_name="user.member"), + models.HumanChatUser, + {"kind": "Human", "nickname": "User", "roleName": "user.member"}, + ), +] + + +def _required_constructor_fields(model): + definition = ast.parse(inspect.getsource(model)).body[0] + constructor = next( + node for node in definition.body if isinstance(node, ast.FunctionDef) and node.name == "__init__" + ) + return { + parameter.arg + for parameter, default in zip(constructor.args.kwonlyargs, constructor.args.kw_defaults) + if default is None + } + + +@pytest.mark.parametrize("request_model,resource_model,lifecycle", REQUEST_PROJECTIONS) +def test_request_fields_follow_resource_lifecycle_visibility(request_model, resource_model, lifecycle): + resource_fields = resource_model({})._attr_to_rest_field + request_fields = request_model({})._attr_to_rest_field + writable_fields = {name for name, field in resource_fields.items() if lifecycle.intersection(field._visibility)} + assert set(request_fields) == writable_fields + for name, field in request_fields.items(): + assert field._rest_name == resource_fields[name]._rest_name + assert lifecycle.intersection(field._visibility) + + required = _required_constructor_fields(resource_model) + if lifecycle == {"update"}: + required = set() + else: + required = {name for name in required & writable_fields if "create" in resource_fields[name]._visibility} + assert _required_constructor_fields(request_model) == required + dictionary_projection = getattr(types, request_model.__name__) + # Discriminated dictionary requests are unions of their concrete subtypes. + for dictionary_type in get_args(dictionary_projection) or (dictionary_projection,): + concrete_request = getattr(models, dictionary_type.__name__) + concrete_fields = concrete_request({})._attr_to_rest_field + assert set(dictionary_type.__annotations__) == {field._rest_name for field in concrete_fields.values()} + required_dictionary_fields = _required_constructor_fields(concrete_request) | { + name for name, field in concrete_fields.items() if field._is_discriminator + } + assert dictionary_type.__required_keys__ == { + concrete_fields[name]._rest_name for name in required_dictionary_fields + } + + +def _response(body): + payload = { + **body, + "id": "returned-id", + "etag": '"new"', + "createdAt": "2026-08-01T00:00:00Z", + "createdBy": "user", + "conversationId": "conversation", + "defaultConversation": "conversation", + } + response = Mock(status_code=200, headers={"content-type": "application/json"}, reason="OK") + response.json.return_value = payload + response.text.return_value = json.dumps(payload) + return Mock(http_response=response) + + +def _assert_request(run, method, body): + request = run.call_args.args[0] + assert json.loads(request.content) == body + assert request.method == ("PATCH" if method.startswith("update_") else "PUT") + assert request.headers["If-Match"] == '"old"' + if method.startswith("update_"): + assert request.headers["Content-Type"] == "application/merge-patch+json" + + +@pytest.mark.parametrize("use_keyword", [False, True]) +@pytest.mark.parametrize("method,args,resource,response_model,body", CASES) +def test_lifecycle_request_and_resource_response(method, args, resource, response_model, body, use_keyword): + with WebPubSubChatServiceClient( + "https://example.webpubsub.azure.com", "chat", AzureKeyCredential("test-key") + ) as client: + with patch.object(client._client._pipeline, "run", return_value=_response(body)) as run: + options = {"etag": '"old"', "match_condition": MatchConditions.IfNotModified} + if use_keyword: + options[BODY_PARAMETERS[method]] = resource + result = getattr(client, method)(*args, **options) + else: + result = getattr(client, method)(*args, resource, **options) + assert isinstance(result, response_model) + assert result.etag == '"new"' + _assert_request(run, method, body) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("use_keyword", [False, True]) +@pytest.mark.parametrize("method,args,resource,response_model,body", CASES) +async def test_async_lifecycle_request_and_resource_response(method, args, resource, response_model, body, use_keyword): + async with AsyncClient("https://example.webpubsub.azure.com", "chat", AzureKeyCredential("test-key")) as client: + with patch.object(client._client._pipeline, "run", new_callable=AsyncMock, return_value=_response(body)) as run: + options = {"etag": '"old"', "match_condition": MatchConditions.IfNotModified} + if use_keyword: + options[BODY_PARAMETERS[method]] = resource + result = await getattr(client, method)(*args, **options) + else: + result = await getattr(client, method)(*args, resource, **options) + assert isinstance(result, response_model) + assert result.etag == '"new"' + _assert_request(run, method, body) + + +@pytest.mark.parametrize("client_type", [WebPubSubChatServiceClient, AsyncClient]) +@pytest.mark.parametrize("method,body_parameter", BODY_PARAMETERS.items()) +def test_resource_specific_body_parameter(client_type, method, body_parameter): + parameters = inspect.signature(getattr(client_type, method)).parameters + assert body_parameter in parameters + assert "resource" not in parameters + + +def test_conversation_room_id_preserves_wire_name(): + conversation = models.ChatConversation(room_id="room") + assert conversation.room_id == "room" + assert conversation.as_dict() == {"parentRoom": "room"} + + +def test_get_conversation_deserializes_room_id(): + with WebPubSubChatServiceClient( + "https://example.webpubsub.azure.com", "chat", AzureKeyCredential("test-key") + ) as client: + with patch.object(client._client._pipeline, "run", return_value=_response({"parentRoom": "room"})): + conversation = client.get_conversation("conversation") + assert isinstance(conversation, models.ChatConversation) + assert conversation.room_id == "room" + assert conversation.id == "returned-id" + assert conversation.etag == '"new"' + + +@pytest.mark.asyncio +async def test_async_get_conversation_deserializes_room_id(): + async with AsyncClient("https://example.webpubsub.azure.com", "chat", AzureKeyCredential("test-key")) as client: + with patch.object( + client._client._pipeline, "run", new_callable=AsyncMock, return_value=_response({"parentRoom": "room"}) + ): + conversation = await client.get_conversation("conversation") + assert isinstance(conversation, models.ChatConversation) + assert conversation.room_id == "room" + assert conversation.id == "returned-id" + assert conversation.etag == '"new"' diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/testcase.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/testcase.py index d3a6da4bbc00..7cdae530cc20 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/testcase.py +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/testcase.py @@ -5,14 +5,7 @@ # -------------------------------------------------------------------------- import functools -from azure.core.exceptions import HttpResponseError from devtools_testutils import AzureRecordedTestCase, PowerShellPreparer - -from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient -from azure.messaging.webpubsubchatservice.aio import ( - WebPubSubChatServiceClient as AsyncWebPubSubChatServiceClient, -) - from chat_message_seed import ( create_chat_message, create_chat_message_async, @@ -20,20 +13,28 @@ verify_chat_connection_async, ) +from azure.core.exceptions import HttpResponseError +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient +from azure.messaging.webpubsubchatservice.aio import ( + WebPubSubChatServiceClient as AsyncWebPubSubChatServiceClient, +) + class WebPubSubChatTest(AzureRecordedTestCase): - def create_client(self, endpoint): + def create_client(self, endpoint, **kwargs): return WebPubSubChatServiceClient( endpoint, "test_hub", self.get_credential(WebPubSubChatServiceClient), + **kwargs, ) - def create_async_client(self, endpoint): + def create_async_client(self, endpoint, **kwargs): return AsyncWebPubSubChatServiceClient( endpoint, "test_hub", self.get_credential(AsyncWebPubSubChatServiceClient, is_async=True), + **kwargs, ) @staticmethod diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/typing/request_models.py b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/typing/request_models.py new file mode 100644 index 000000000000..1eb285ab1faf --- /dev/null +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tests/typing/request_models.py @@ -0,0 +1,100 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Static type-checking fixture, not an executable test or customer sample. + +From the package directory, run: +python -m mypy --python-version 3.10 --ignore-missing-imports --warn-unused-ignores tests/typing/request_models.py + +The negative cases must remain type errors; warn-unused-ignores detects them +becoming incorrectly accepted. Runtime request tests live in test_request_models.py. +""" + +from azure.messaging.webpubsubchatservice import WebPubSubChatServiceClient, models, types +from azure.messaging.webpubsubchatservice.aio import WebPubSubChatServiceClient as AsyncClient + + +def check_request_types(client: WebPubSubChatServiceClient) -> None: + client.update_topic("room", "topic", topic=models.ChatTopicUpdate(title="New title")) + client.update_topic("room", "topic", models.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED)) + client.update_topic("room", "topic", types.ChatTopicUpdate(title="New title")) + client.update_topic("room", "topic", types.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED)) + client.update_message("conversation", "message", message=models.ChatMessageUpdate(created_by="user")) + client.update_message( + "conversation", "message", models.ChatMessageUpdate(content=models.MessageContent(text="Updated")) + ) + client.update_message("conversation", "message", types.ChatMessageUpdate(createdBy="user")) + client.update_message("conversation", "message", types.ChatMessageUpdate(content={"text": "Updated"})) + client.create_or_replace_topic("room", "topic", topic=models.ChatTopicCreateOrReplace(title="Title")) + client.create_or_replace_topic( + "room", "topic", models.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED) + ) + client.create_or_replace_topic("room", "topic", types.ChatTopicCreateOrReplace(title="Title")) + client.create_or_replace_topic( + "room", "topic", types.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED) + ) + client.create_or_replace_role("user.role", role=models.ChatRoleCreateOrReplace(permissions=["user.create_room"])) + client.create_or_replace_role("user.role", types.ChatRoleCreateOrReplace(permissions=["user.create_room"])) + client.create_or_replace_room("room", room=models.ChatRoomCreateOrReplace(title="Room")) + client.create_or_replace_room("room", types.ChatRoomCreateOrReplace(title="Room")) + client.create_or_replace_room_member( + "room", "user", member=models.ChatRoomMemberCreateOrReplace(role_name="room.member") + ) + client.create_or_replace_room_member("room", "user", types.ChatRoomMemberCreateOrReplace(roleName="room.member")) + user: models.ChatUserCreateOrReplace = models.HumanChatUserCreateOrReplace(nickname="User", role_name="user.member") + user_dict: types.ChatUserCreateOrReplace = types.HumanChatUserCreateOrReplace( + kind=models.ChatUserKind.HUMAN, nickname="User", roleName="user.member" + ) + client.create_or_replace_user("user", user=user) + client.create_or_replace_user("user", user_dict) + models.ChatTopicUpdate(title=123) # type: ignore[call-overload] + models.ChatTopicUpdate(state=123) # type: ignore[call-overload] + models.ChatTopicUpdate(id="read-only") # type: ignore[call-overload] + models.ChatMessageUpdate(created_by=123) # type: ignore[call-overload] + models.ChatMessageUpdate(content=123) # type: ignore[call-overload] + models.ChatTopicCreateOrReplace() # type: ignore[call-overload] + models.ChatTopicCreateOrReplace(title="Title", state=123) # type: ignore[call-overload] + types.ChatTopicUpdate(title=123) # type: ignore[typeddict-item] + types.ChatTopicUpdate(id="read-only") # type: ignore[typeddict-unknown-key] + types.ChatMessageUpdate(content=123) # type: ignore[typeddict-item] + types.ChatTopicCreateOrReplace() # type: ignore[typeddict-item] + + +async def check_async_request_types(client: AsyncClient) -> None: + await client.create_or_replace_topic("room", "topic", topic=models.ChatTopicCreateOrReplace(title="Title")) + await client.create_or_replace_topic( + "room", "topic", models.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED) + ) + await client.create_or_replace_topic("room", "topic", types.ChatTopicCreateOrReplace(title="Title")) + await client.create_or_replace_topic( + "room", "topic", types.ChatTopicCreateOrReplace(title="Title", state=models.ChatTopicState.ARCHIVED) + ) + await client.create_or_replace_role( + "user.role", role=models.ChatRoleCreateOrReplace(permissions=["user.create_room"]) + ) + await client.create_or_replace_role("user.role", types.ChatRoleCreateOrReplace(permissions=["user.create_room"])) + await client.create_or_replace_room("room", room=models.ChatRoomCreateOrReplace(title="Room")) + await client.create_or_replace_room("room", types.ChatRoomCreateOrReplace(title="Room")) + await client.create_or_replace_room_member( + "room", "user", member=models.ChatRoomMemberCreateOrReplace(role_name="room.member") + ) + await client.create_or_replace_room_member( + "room", "user", types.ChatRoomMemberCreateOrReplace(roleName="room.member") + ) + user: models.ChatUserCreateOrReplace = models.HumanChatUserCreateOrReplace(nickname="User", role_name="user.member") + user_dict: types.ChatUserCreateOrReplace = types.HumanChatUserCreateOrReplace( + kind=models.ChatUserKind.HUMAN, nickname="User", roleName="user.member" + ) + await client.create_or_replace_user("user", user=user) + await client.create_or_replace_user("user", user_dict) + await client.update_topic("room", "topic", topic=models.ChatTopicUpdate(title="New title")) + await client.update_topic("room", "topic", models.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED)) + await client.update_topic("room", "topic", types.ChatTopicUpdate(title="New title")) + await client.update_topic("room", "topic", types.ChatTopicUpdate(state=models.ChatTopicState.ARCHIVED)) + await client.update_message("conversation", "message", message=models.ChatMessageUpdate(created_by="user")) + await client.update_message( + "conversation", "message", models.ChatMessageUpdate(content=models.MessageContent(text="Updated")) + ) + await client.update_message("conversation", "message", types.ChatMessageUpdate(createdBy="user")) + await client.update_message("conversation", "message", types.ChatMessageUpdate(content={"text": "Updated"})) diff --git a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tsp-location.yaml b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tsp-location.yaml index fcf7c01e091f..e72fd63006c3 100644 --- a/sdk/webpubsub/azure-messaging-webpubsubchatservice/tsp-location.yaml +++ b/sdk/webpubsub/azure-messaging-webpubsubchatservice/tsp-location.yaml @@ -1,5 +1,5 @@ directory: specification/webpubsub/data-plane/WebPubSubChat -commit: 15202f7ede10316a7cecc6911f887eb8655f87ce -repo: Azure/azure-rest-api-specs +commit: f3aa40d8afd5474f09689233dadd045372395ac1 +repo: Y-Sindo/azure-rest-api-specs # cspell:ignore Sindo additionalDirectories: [] -entrypointFile: client.tsp +entrypointFile: client.tsp \ No newline at end of file