diff --git a/core/docs/ApiClient-Design.md b/core/docs/ApiClient-Design.md index ae8cbb89..c1927910 100644 --- a/core/docs/ApiClient-Design.md +++ b/core/docs/ApiClient-Design.md @@ -9,20 +9,22 @@ ``` ApiClient (top-level facade) ├── Conversations → ConversationApiClient [→ core ConversationClient] -│ ├── Activities → ActivityClient -│ ├── Members → MemberClient -│ └── Reactions → ReactionClient [Experimental] +│ ├── Activities → [deprecated] ActivityClient +│ ├── Members → [deprecated] MemberClient +│ └── Reactions → [deprecated] ReactionClient ├── Users → UserTokenApiClient [token + sign-in; → core UserTokenClient] ├── Teams → TeamClient [BotHttpClient → serviceUrl/v3/teams/] └── Meetings → MeetingClient [BotHttpClient → serviceUrl/v1/meetings/] ``` +`ConversationApiClient` now exposes activity, member, and reaction methods directly (e.g. `CreateActivityAsync`, `GetMembersPagedAsync`, `AddReactionAsync`). The sub-client properties (`Activities`, `Members`, `Reactions`) are retained but marked `[Obsolete]` for backward compatibility. + | Sub-client | Backed by | Why | |---|---|---| | Conversations, Users | core `ConversationClient` / `UserTokenClient` | Single source of truth for URL construction, auth, agents-channel handling, agentic identity, headers, and logging | | Teams, Meetings | `BotHttpClient` directly | No core client exists for these endpoints | -**Experimental APIs:** `ReactionClient` (`ExperimentalTeamsReactions`); `ActivityClient.CreateTargetedAsync` / `UpdateTargetedAsync` / `DeleteTargetedAsync` (`ExperimentalTeamsTargeted`, not supported in team channels). +**Experimental APIs:** `ConversationApiClient.CreateTargetedActivityAsync` / `UpdateTargetedActivityAsync` (`ExperimentalTeamsTargeted`, not supported in team channels). ## Construction & scoping @@ -36,7 +38,7 @@ In handlers, use `ctx.Api`, which lazily scopes to `Activity.ServiceUrl`: ```csharp botApp.OnMessage(async (ctx, ct) => { - var members = await ctx.Api.Conversations.Members.GetAsync(conversationId, ct); + var members = await ctx.Api.Conversations.GetMembersPagedAsync(conversationId, cancellationToken: ct); var team = await ctx.Api.Teams.GetByIdAsync(teamId, ct); }); ``` @@ -54,10 +56,10 @@ Sub-clients are thin adapters: they bridge the Libraries-style `(conversationId, ``` core/src/Microsoft.Teams.Apps/Api/Clients/ ├── ApiClient.cs Facade, DI entry point, ForServiceUrl factory -├── ConversationApiClient.cs Conversation facade → core ConversationClient -├── ActivityClient.cs Activity CRUD + targeted → core ConversationClient -├── MemberClient.cs Member operations → core ConversationClient -├── ReactionClient.cs Reaction operations → core ConversationClient [Experimental] +├── ConversationApiClient.cs Conversation facade → core ConversationClient (activities, members, reactions) +├── ActivityClient.cs [deprecated] Activity CRUD + targeted → core ConversationClient +├── MemberClient.cs [deprecated] Member operations → core ConversationClient +├── ReactionClient.cs [deprecated] Reaction operations → core ConversationClient ├── TeamClient.cs Team info → BotHttpClient (v3/teams/) ├── MeetingClient.cs Meeting info → BotHttpClient (v1/meetings/) + models └── UserTokenApiClient.cs User token + sign-in → core UserTokenClient diff --git a/core/samples/TargetedMessages/Program.cs b/core/samples/TargetedMessages/Program.cs index e07431e5..9d4a77e3 100644 --- a/core/samples/TargetedMessages/Program.cs +++ b/core/samples/TargetedMessages/Program.cs @@ -58,7 +58,7 @@ try { MessageActivity updated = new($"✏️ Updated at {DateTime.UtcNow:HH:mm:ss}"); - await context.Api.Conversations.Activities.UpdateTargetedAsync(conversationId, messageId, updated, cancellationToken: cancellationToken); + await context.Api.Conversations.UpdateTargetedActivityAsync(conversationId, messageId, updated, cancellationToken: cancellationToken); } catch (Exception ex) { @@ -88,7 +88,7 @@ await Task.Delay(3000); try { - await context.Api.Conversations.Activities.DeleteTargetedAsync(conversationId, messageId, cancellationToken: cancellationToken); + await context.Api.Conversations.DeleteTargetedActivityAsync(conversationId, messageId, cancellationToken: cancellationToken); } catch (Exception ex) { diff --git a/core/samples/TeamsBot/Program.cs b/core/samples/TeamsBot/Program.cs index c9416a43..6fc68862 100644 --- a/core/samples/TeamsBot/Program.cs +++ b/core/samples/TeamsBot/Program.cs @@ -182,7 +182,7 @@ [Visit Microsoft](https://www.microsoft.com) .WithServiceUrl(context.Activity.ServiceUrl) .Build(); - await context.Api.Conversations.Activities.UpdateTargetedAsync( + await context.Api.Conversations.UpdateTargetedActivityAsync( context.Activity.Conversation.Id!, sendResponse!.Id!, updated, @@ -191,7 +191,7 @@ await context.Api.Conversations.Activities.UpdateTargetedAsync( await Task.Delay(2000, cancellationToken); // Delete the targeted message - await context.Api.Conversations.Activities.DeleteTargetedAsync( + await context.Api.Conversations.DeleteTargetedActivityAsync( context.Activity.Conversation.Id!, sendResponse.Id!, cancellationToken: cancellationToken); @@ -217,7 +217,7 @@ await context.Api.Conversations.Activities.DeleteTargetedAsync( await Task.Delay(2000, cancellationToken); // Add a waving hand reaction - await context.Api.Conversations.Reactions.AddAsync( + await context.Api.Conversations.AddReactionAsync( context.Activity.Conversation.Id, response!.Id!, "1f44b_wavinghand-tone4", @@ -227,7 +227,7 @@ await context.Api.Conversations.Reactions.AddAsync( await Task.Delay(2000, cancellationToken); // Add a beaming face reaction - await context.Api.Conversations.Reactions.AddAsync( + await context.Api.Conversations.AddReactionAsync( context?.Activity?.Conversation?.Id!, response.Id!, "1f601_beamingfacewithsmilingeyes", @@ -237,7 +237,7 @@ await context.Api.Conversations.Reactions.AddAsync( await Task.Delay(2000, cancellationToken); ArgumentNullException.ThrowIfNull(context); // Remove the beaming face reaction - await context.Api.Conversations.Reactions.DeleteAsync( + await context.Api.Conversations.DeleteReactionAsync( context?.Activity?.Conversation?.Id!, response.Id!, "1f601_beamingfacewithsmilingeyes", diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs index bd526108..1cf236fa 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs @@ -14,6 +14,7 @@ namespace Microsoft.Teams.Apps.Api.Clients; /// Client for creating, updating, and deleting activities in a conversation. /// Delegates to the core . /// +[Obsolete("Use the activity methods on ConversationApiClient directly instead.")] public class ActivityClient { private readonly CoreConversationClient _client; diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs index 03cbc0c4..26b49bd0 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Microsoft.Teams.Apps.Schema; using Microsoft.Teams.Core; using Microsoft.Teams.Core.Http; using Microsoft.Teams.Core.Schema; @@ -10,7 +13,7 @@ namespace Microsoft.Teams.Apps.Api.Clients; /// -/// Client for managing conversations, exposing sub-clients for activities, members, and reactions. +/// Client for managing conversations, including activities, members, and reactions. /// Delegates to the core . /// public class ConversationApiClient @@ -21,25 +24,30 @@ public class ConversationApiClient /// /// Client for activity operations. /// + [Obsolete("Use the activity methods on ConversationApiClient directly instead.")] public ActivityClient Activities { get; } /// /// Client for member operations. /// + [Obsolete("Use the member methods on ConversationApiClient directly instead.")] public MemberClient Members { get; } /// /// Client for reaction operations. /// + [Obsolete("Use ConversationApiClient.AddReactionAsync and ConversationApiClient.DeleteReactionAsync instead.")] public ReactionClient Reactions { get; } internal ConversationApiClient(Uri serviceUrl, CoreConversationClient client) { _serviceUrl = serviceUrl; _client = client; +#pragma warning disable CS0618 // Suppress obsolete warnings for backward-compatible initialization Activities = new ActivityClient(serviceUrl, client); Members = new MemberClient(serviceUrl, client); Reactions = new ReactionClient(serviceUrl, client); +#pragma warning restore CS0618 } /// @@ -49,4 +57,218 @@ public Task CreateAsync(ConversationParameters reque { return _client.CreateConversationAsync(request, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); } + + #region Activity Methods + + /// + /// Create a new activity in a conversation. + /// + public Task CreateActivityAsync(string conversationId, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activity); + activity.ServiceUrl ??= _serviceUrl; + activity.Conversation ??= new Conversation(conversationId); + return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Update an existing activity in a conversation. + /// + public Task UpdateActivityAsync(string conversationId, string id, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activity); + activity.ServiceUrl ??= _serviceUrl; + return _client.UpdateActivityAsync(conversationId, id, activity, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Reply to an existing activity in a conversation. + /// + public Task ReplyToActivityAsync(string conversationId, string id, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activity); + activity.ReplyToId = id; + activity.ServiceUrl ??= _serviceUrl; + activity.Conversation ??= new Conversation(conversationId); + return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Delete an activity from a conversation. + /// + public Task DeleteActivityAsync(string conversationId, string id, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + return _client.DeleteActivityAsync(conversationId, id, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Create a new targeted activity in a conversation. + /// Targeted activities are only visible to the specified recipient. + /// + [Experimental("ExperimentalTeamsTargeted")] + public Task CreateTargetedActivityAsync(string conversationId, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activity); + activity.ServiceUrl ??= _serviceUrl; + activity.Conversation ??= new Conversation(conversationId); + if (activity.Recipient is not null) + { + activity.Recipient.IsTargeted = true; + } + return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Update an existing targeted activity in a conversation. + /// + [Experimental("ExperimentalTeamsTargeted")] + public Task UpdateTargetedActivityAsync(string conversationId, string id, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(activity); + activity.ServiceUrl ??= _serviceUrl; + return _client.UpdateTargetedActivityAsync(conversationId, id, activity, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Delete a targeted activity from a conversation. + /// + public Task DeleteTargetedActivityAsync(string conversationId, string id, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + return _client.DeleteTargetedActivityAsync(conversationId, id, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + #endregion + + #region Member Methods + + /// + /// Get all members of a conversation. + /// + [Obsolete("Use GetMembersPagedAsync instead.")] + public async Task> GetMembersAsync(string conversationId, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + IList members = await _client.GetConversationMembersAsync(conversationId, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); + return [.. members.Select(m => TeamsChannelAccount.FromChannelAccount(m))]; + } + + /// + /// Get members of a conversation with pagination support. + /// + public async Task GetMembersPagedAsync( + string conversationId, + int pageSize = 50, + string? continuationToken = null, + AgenticIdentity? agenticIdentity = null, + Dictionary? additionalHeaders = null, + CancellationToken cancellationToken = default) + { + PagedMembersResult? paged = await _client.GetConversationPagedMembersAsync( + conversationId, + _serviceUrl, + pageSize, + continuationToken, + requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), + customHeaders: additionalHeaders, + cancellationToken: cancellationToken).ConfigureAwait(false); + PagedTeamsMembersResult result = new(); + if (paged is not null) + { + result.ContinuationToken = paged.ContinuationToken; + if (paged.Members is not null) + { + result.Members = [.. paged.Members.Select(m => TeamsChannelAccount.FromChannelAccount(m))]; + } + } + return result; + } + + /// + /// Get all members of a conversation, automatically following pagination. + /// + /// + /// Streams members across all pages by following the continuation token internally, + /// for convenient await foreach iteration. Use when you + /// need explicit control over paging (for example, to persist the continuation token across + /// requests and resume later). + /// + public async IAsyncEnumerable GetAllMembersAsync( + string conversationId, + int pageSize = 50, + AgenticIdentity? agenticIdentity = null, + Dictionary? additionalHeaders = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + string? ct = null; + do + { + PagedTeamsMembersResult page = await GetMembersPagedAsync( + conversationId, + pageSize, + ct, + agenticIdentity, + additionalHeaders, + cancellationToken).ConfigureAwait(false); + + foreach (TeamsChannelAccount? member in page.Members) + { + if (member is not null) + { + yield return member; + } + } + + ct = page.ContinuationToken; + } while (!string.IsNullOrEmpty(ct)); + } + + /// + /// Get a specific member of a conversation by ID. + /// + public Task GetMemberByIdAsync(string conversationId, string memberId, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) where T : ChannelAccount + { + return _client.GetConversationMemberAsync(conversationId, memberId, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Get a specific member of a conversation by ID. + /// + public async Task GetMemberByIdAsync(string conversationId, string memberId, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + ChannelAccount member = await GetMemberByIdAsync(conversationId, memberId, agenticIdentity, additionalHeaders, cancellationToken).ConfigureAwait(false); + return TeamsChannelAccount.FromChannelAccount(member); + } + + #endregion + + #region Reaction Methods + + /// + /// Adds a reaction on an activity in a conversation. + /// + /// The conversation id. + /// The id of the activity to react to. + /// The reaction type (for example: "like", "heart", "laugh", etc.). + /// Optional agentic identity for authentication. + /// Optional custom headers to include in the request. + /// A to observe while waiting for the task to complete. + public Task AddReactionAsync(string conversationId, string activityId, string reactionType, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + return _client.AddReactionAsync(conversationId, activityId, reactionType, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + /// + /// Removes a reaction from an activity in a conversation. + /// + /// The conversation id. + /// The id of the activity the reaction is on. + /// The reaction type to remove (for example: "like", "heart", "laugh", etc.). + /// Optional agentic identity for authentication. + /// Optional custom headers to include in the request. + /// A to observe while waiting for the task to complete. + public Task DeleteReactionAsync(string conversationId, string activityId, string reactionType, AgenticIdentity? agenticIdentity = null, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) + { + return _client.DeleteReactionAsync(conversationId, activityId, reactionType, _serviceUrl, requestContext: BotRequestContext.FromAgenticIdentity(agenticIdentity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); + } + + #endregion } diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/MemberClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/MemberClient.cs index ed5346ba..cfd05b82 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/MemberClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/MemberClient.cs @@ -15,6 +15,7 @@ namespace Microsoft.Teams.Apps.Api.Clients; /// Client for managing conversation members. /// Delegates to the core . /// +[Obsolete("Use the member methods on ConversationApiClient directly instead.")] public class MemberClient { private readonly CoreConversationClient _client; diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ReactionClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ReactionClient.cs index b016c8da..88419ec9 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ReactionClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ReactionClient.cs @@ -3,6 +3,7 @@ using Microsoft.Teams.Core.Http; using Microsoft.Teams.Core.Schema; + using CoreConversationClient = Microsoft.Teams.Core.ConversationClient; namespace Microsoft.Teams.Apps.Api.Clients; @@ -11,6 +12,7 @@ namespace Microsoft.Teams.Apps.Api.Clients; /// Client for managing reactions on activities in a conversation. /// Delegates to the core . /// +[Obsolete("Use ConversationApiClient.AddReactionAsync and ConversationApiClient.DeleteReactionAsync instead.")] public class ReactionClient { private readonly CoreConversationClient _client; diff --git a/core/test/IntegrationTests/ApiClientTests.cs b/core/test/IntegrationTests/ApiClientTests.cs index 38c2578a..3cb53f9a 100644 --- a/core/test/IntegrationTests/ApiClientTests.cs +++ b/core/test/IntegrationTests/ApiClientTests.cs @@ -53,7 +53,7 @@ public async Task Activities_CreateAsync() { CoreActivity activity = CreateMessageActivity($"[ApiClient.Activities.Create] at `{DateTime.UtcNow:s}`"); - SendActivityResponse? res = await _api.Conversations.Activities.CreateAsync(_f.ConversationId, activity); + SendActivityResponse? res = await _api.Conversations.CreateActivityAsync(_f.ConversationId, activity); Assert.NotNull(res); Assert.NotNull(res.Id); @@ -66,12 +66,12 @@ public async Task Activities_UpdateAsync() { CoreActivity original = CreateMessageActivity($"[ApiClient.Activities.Update] Original at `{DateTime.UtcNow:s}`"); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateAsync(_f.ConversationId, original); + SendActivityResponse? sent = await _api.Conversations.CreateActivityAsync(_f.ConversationId, original); Assert.NotNull(sent?.Id); CoreActivity updated = CreateMessageActivity($"[ApiClient.Activities.Update] Updated at `{DateTime.UtcNow:s}`"); - UpdateActivityResponse? res = await _api.Conversations.Activities.UpdateAsync( + UpdateActivityResponse? res = await _api.Conversations.UpdateActivityAsync( _f.ConversationId, sent.Id, updated); Assert.NotNull(res?.Id); @@ -84,12 +84,12 @@ public async Task Activities_ReplyAsync() { CoreActivity original = CreateMessageActivity($"[ApiClient.Activities.Reply] Parent at `{DateTime.UtcNow:s}`"); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateAsync(_f.ConversationId, original); + SendActivityResponse? sent = await _api.Conversations.CreateActivityAsync(_f.ConversationId, original); Assert.NotNull(sent?.Id); CoreActivity reply = CreateMessageActivity($"[ApiClient.Activities.Reply] Reply at `{DateTime.UtcNow:s}`"); - SendActivityResponse? res = await _api.Conversations.Activities.ReplyAsync( + SendActivityResponse? res = await _api.Conversations.ReplyToActivityAsync( _f.ConversationId, sent.Id, reply); Assert.NotNull(res); @@ -102,12 +102,12 @@ public async Task Activities_DeleteAsync() { CoreActivity activity = CreateMessageActivity($"[ApiClient.Activities.Delete] at `{DateTime.UtcNow:s}`"); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateAsync(_f.ConversationId, activity); + SendActivityResponse? sent = await _api.Conversations.CreateActivityAsync(_f.ConversationId, activity); Assert.NotNull(sent?.Id); await Task.Delay(2000); - await _api.Conversations.Activities.DeleteAsync(_f.ConversationId, sent.Id, _f.AgenticIdentity); + await _api.Conversations.DeleteActivityAsync(_f.ConversationId, sent.Id, _f.AgenticIdentity); _output.WriteLine($"Deleted activity: {sent.Id}"); } @@ -125,7 +125,7 @@ public async Task Activities_CreateTargetedAsync() $"[ApiClient.Activities.CreateTargeted] at `{DateTime.UtcNow:s}`", new ChannelAccount { Id = _f.MemberMri1 }); - SendActivityResponse? res = await _api.Conversations.Activities.CreateTargetedAsync(_f.ConversationId, activity); + SendActivityResponse? res = await _api.Conversations.CreateTargetedActivityAsync(_f.ConversationId, activity); Assert.NotNull(res); Assert.NotNull(res.Id); @@ -141,12 +141,12 @@ public async Task Activities_UpdateTargetedAsync() $"[ApiClient.Activities.UpdateTargeted] Original at `{DateTime.UtcNow:s}`", new ChannelAccount { Id = _f.MemberMri1 }); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateTargetedAsync(_f.ConversationId, original); + SendActivityResponse? sent = await _api.Conversations.CreateTargetedActivityAsync(_f.ConversationId, original); Assert.NotNull(sent?.Id); CoreActivity updated = CreateMessageActivity($"[ApiClient.Activities.UpdateTargeted] Updated at `{DateTime.UtcNow:s}`"); - UpdateActivityResponse? res = await _api.Conversations.Activities.UpdateTargetedAsync( + UpdateActivityResponse? res = await _api.Conversations.UpdateTargetedActivityAsync( _f.ConversationId, sent.Id, updated); Assert.NotNull(res?.Id); @@ -162,12 +162,12 @@ public async Task Activities_DeleteTargetedAsync() $"[ApiClient.Activities.DeleteTargeted] at `{DateTime.UtcNow:s}`", new ChannelAccount { Id = _f.MemberMri1 }); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateTargetedAsync(_f.ConversationId, activity); + SendActivityResponse? sent = await _api.Conversations.CreateTargetedActivityAsync(_f.ConversationId, activity); Assert.NotNull(sent?.Id); await Task.Delay(2000); - await _api.Conversations.Activities.DeleteTargetedAsync(_f.ConversationId, sent.Id, _f.AgenticIdentity); + await _api.Conversations.DeleteTargetedActivityAsync(_f.ConversationId, sent.Id, _f.AgenticIdentity); _output.WriteLine($"Deleted targeted activity: {sent.Id}"); } @@ -179,7 +179,7 @@ public async Task Activities_DeleteTargetedAsync() [Trait("Category", "Members")] public async Task Members_GetAsync() { - IList members = await _api.Conversations.Members.GetAsync(_f.ConversationId, _f.AgenticIdentity); + IList members = await _api.Conversations.GetMembersAsync(_f.ConversationId, _f.AgenticIdentity); Assert.NotNull(members); Assert.NotEmpty(members); @@ -196,7 +196,7 @@ public async Task Members_GetPagedAsync() { Skip.If(_f.AgenticIdentity is not null, "Paged members returns 500 with agentic identity — service limitation"); - PagedTeamsMembersResult paged = await _api.Conversations.Members.GetPagedAsync(_f.ConversationId, agenticIdentity: _f.AgenticIdentity); + PagedTeamsMembersResult paged = await _api.Conversations.GetMembersPagedAsync(_f.ConversationId, agenticIdentity: _f.AgenticIdentity); Assert.NotNull(paged); Assert.NotEmpty(paged.Members); @@ -214,7 +214,7 @@ public async Task Members_GetByIdAsync() { string memberId = _f.MemberMri1!; - TeamsChannelAccount? member = await _api.Conversations.Members.GetByIdAsync( + TeamsChannelAccount? member = await _api.Conversations.GetMemberByIdAsync( _f.ConversationId, memberId, _f.AgenticIdentity); Assert.NotNull(member); @@ -228,7 +228,7 @@ public async Task Members_GetByIdAsync_AsTeamsChannelAccount() { string memberId = _f.MemberMri1!; - TeamsChannelAccount member = await _api.Conversations.Members.GetByIdAsync( + TeamsChannelAccount member = await _api.Conversations.GetMemberByIdAsync( _f.ConversationId, memberId, _f.AgenticIdentity); Assert.NotNull(member); @@ -249,15 +249,15 @@ public async Task Reactions_AddAndDelete() CoreActivity activity = CreateMessageActivity($"[ApiClient.Reactions] Test at `{DateTime.UtcNow:s}`"); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateAsync(_f.ConversationId, activity); + SendActivityResponse? sent = await _api.Conversations.CreateActivityAsync(_f.ConversationId, activity); Assert.NotNull(sent?.Id); - await _api.Conversations.Reactions.AddAsync(_f.ConversationId, sent.Id, "like", _f.AgenticIdentity); + await _api.Conversations.AddReactionAsync(_f.ConversationId, sent.Id, "like", _f.AgenticIdentity); _output.WriteLine("Added 'like' reaction"); await Task.Delay(1000); - await _api.Conversations.Reactions.DeleteAsync(_f.ConversationId, sent.Id, "like", _f.AgenticIdentity); + await _api.Conversations.DeleteReactionAsync(_f.ConversationId, sent.Id, "like", _f.AgenticIdentity); _output.WriteLine("Removed 'like' reaction"); } @@ -325,8 +325,7 @@ public async Task Meetings_GetParticipantAsync() break; } // If not available on the cached list, fetch full details for this member - TeamsChannelAccount tm = await _api.Conversations.Members - .GetByIdAsync(_f.ConversationId, m.Id, _f.AgenticIdentity); + TeamsChannelAccount tm = await _api.Conversations.GetMemberByIdAsync(_f.ConversationId, m.Id, _f.AgenticIdentity); if (tm.AadObjectId is not null) { aadObjectId = tm.AadObjectId; @@ -468,7 +467,7 @@ public async Task ForServiceUrl_CreatesScopedClient() Assert.Equal(_f.ServiceUrl, scoped.ServiceUrl); // Verify the scoped client can make a real call - IList members = await scoped.Conversations.Members.GetAsync(_f.ConversationId, _f.AgenticIdentity); + IList members = await scoped.Conversations.GetMembersAsync(_f.ConversationId, _f.AgenticIdentity); Assert.NotNull(members); Assert.NotEmpty(members); _output.WriteLine($"ForServiceUrl scoped client retrieved {members.Count} members"); diff --git a/core/test/IntegrationTests/CompatTeamsInfoTests.cs b/core/test/IntegrationTests/CompatTeamsInfoTests.cs index 7699f798..6102b7bb 100644 --- a/core/test/IntegrationTests/CompatTeamsInfoTests.cs +++ b/core/test/IntegrationTests/CompatTeamsInfoTests.cs @@ -307,7 +307,7 @@ public async Task GetMeetingParticipantAsync_ReturnsParticipant() // The meetings participant API requires AAD object ID, not MRI/pairwise bot framework ID. // Get the AAD object ID from a human member (bots don't have one). ApiClient api = _f.ScopedApiClient; - IList members = await api.Conversations.Members.GetAsync(_f.ConversationId, _f.AgenticIdentity); + IList members = await api.Conversations.GetMembersAsync(_f.ConversationId, _f.AgenticIdentity); Assert.NotEmpty(members); string? aadObjectId = null; diff --git a/core/test/IntegrationTests/CreateConversationTests.cs b/core/test/IntegrationTests/CreateConversationTests.cs index f78315fe..88dc1bbf 100644 --- a/core/test/IntegrationTests/CreateConversationTests.cs +++ b/core/test/IntegrationTests/CreateConversationTests.cs @@ -272,7 +272,7 @@ public async Task ApiClient_CreatePersonalChat_AndSendViaActivities() .WithProperty("text", $"[ApiClient] 1:1 via Activities.Create at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _api.Conversations.Activities.CreateAsync(response.Id, activity); + SendActivityResponse? sent = await _api.Conversations.CreateActivityAsync(response.Id, activity); Assert.NotNull(sent?.Id); _output.WriteLine($"[ApiClient] Created 1:1 {response.Id}, sent activity {sent.Id}"); } @@ -365,7 +365,7 @@ public async Task ApiClient_CreateChannelThread_AndReply() .WithProperty("text", $"[ApiClient] Thread reply at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? replyResponse = await _api.Conversations.Activities.ReplyAsync( + SendActivityResponse? replyResponse = await _api.Conversations.ReplyToActivityAsync( response.Id, response.ActivityId, reply); Assert.NotNull(replyResponse); diff --git a/core/test/IntegrationTests/IntegrationTestFixture.cs b/core/test/IntegrationTests/IntegrationTestFixture.cs index fa761783..3d294042 100644 --- a/core/test/IntegrationTests/IntegrationTestFixture.cs +++ b/core/test/IntegrationTests/IntegrationTestFixture.cs @@ -122,7 +122,7 @@ public IntegrationTestFixture() public async Task InitializeAsync() { ApiClient scoped = ScopedApiClient; - IList raw = await scoped.Conversations.Members.GetAsync(ConversationId, AgenticIdentity); + IList raw = await scoped.Conversations.GetMembersAsync(ConversationId, AgenticIdentity); string botMri = $"28:{BotAppId}"; CachedMembers = raw diff --git a/core/test/TeamsApisDemo/Program.cs b/core/test/TeamsApisDemo/Program.cs index a69b1238..627f44ca 100644 --- a/core/test/TeamsApisDemo/Program.cs +++ b/core/test/TeamsApisDemo/Program.cs @@ -26,18 +26,18 @@ var smba = new Uri("https://smba.trafficmanager.net/amer"); -var membersClient = teamsBotApplication.Api.ForServiceUrl(smba).Conversations.Members; +var conversations = teamsBotApplication.Api.ForServiceUrl(smba).Conversations; int pages = 1; string cid = "19%3ALydFnezGKSkhYoiLNP6kZ8AuXQr36EDAkvG9CNJSPKc1%40thread.tacv2"; -var paged = await membersClient.GetPagedAsync(cid, 52); +var paged = await conversations.GetMembersPagedAsync(cid, 52); List members = [..paged.Members]; while (!string.IsNullOrEmpty(paged.ContinuationToken)) { Console.WriteLine("Getting next page of members..."); - paged = await membersClient.GetPagedAsync(cid, 52, paged.ContinuationToken); + paged = await conversations.GetMembersPagedAsync(cid, 52, paged.ContinuationToken); members.AddRange(paged.Members); pages++; }