diff --git a/core/samples/A2ABot/A2A/A2AServer.cs b/core/samples/A2ABot/A2A/A2AServer.cs index 294c5f43..d80a696d 100644 --- a/core/samples/A2ABot/A2A/A2AServer.cs +++ b/core/samples/A2ABot/A2A/A2AServer.cs @@ -73,7 +73,7 @@ public async Task ExecuteAsync(RequestContext context, AgentEventQueue eventQueu .WithServiceUrl(serviceUrl) .WithConversation(new TeamsConversation { Id = newConvId }) .Build(); - SendActivityResponse? sent = await conversations.SendActivityAsync(proactive, cancellationToken: ct); + SendActivityResponse? sent = await conversations.SendActivityAsync(proactive, serviceUrl, cancellationToken: ct); logger.LogInformation("[{Bot}/A2A] proactive greeting sent (conv={ConvId}, activityId={ActivityId})", config.Name, newConvId, sent?.Id ?? ""); diff --git a/core/samples/AFBot/Program.cs b/core/samples/AFBot/Program.cs index fe7204a3..6bbb1deb 100644 --- a/core/samples/AFBot/Program.cs +++ b/core/samples/AFBot/Program.cs @@ -40,12 +40,11 @@ CoreActivity typing = CoreActivity.CreateBuilder() .WithType(ActivityType.Typing) - .WithServiceUrl(activity.ServiceUrl!) .WithChannelId(activity.ChannelId!) .WithConversation(activity.Conversation!) .WithFrom(activity.Recipient) .Build(); - await botApp.SendActivityAsync(typing, cancellationToken: cancellationToken); + await botApp.SendActivityAsync(typing, activity.ServiceUrl!, cancellationToken: cancellationToken); AgentRunResponse agentResponse = await agent.RunAsync(activity.Properties["text"]?.ToString() ?? "OMW", cancellationToken: timer.Token); @@ -53,14 +52,13 @@ Console.WriteLine($"AI:: GOT {agentResponse.Messages.Count} msgs"); CoreActivity replyActivity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) - .WithServiceUrl(activity.ServiceUrl!) .WithChannelId(activity.ChannelId!) .WithConversation(activity.Conversation!) .WithFrom(activity.Recipient) .WithProperty("text", m1!.Text) .Build(); - SendActivityResponse? res = await botApp.SendActivityAsync(replyActivity, cancellationToken: cancellationToken); + SendActivityResponse? res = await botApp.SendActivityAsync(replyActivity, activity.ServiceUrl!, cancellationToken: cancellationToken); Console.WriteLine("SENT >>> => " + res?.Id); }; diff --git a/core/samples/CompatBot/EchoBot.cs b/core/samples/CompatBot/EchoBot.cs index 042d1ee6..f78ab927 100644 --- a/core/samples/CompatBot/EchoBot.cs +++ b/core/samples/CompatBot/EchoBot.cs @@ -84,11 +84,12 @@ protected override async Task OnMessageActivityAsync(ITurnContext Notify( .WithConversation(new Conversation(conversationId)) .WithText(message) .Build(); - await app.SendActivityAsync(notifyActivity, cancellationToken); + await app.SendActivityAsync(notifyActivity, state.ServiceUrl, cancellationToken); return new NotifyResult(Notified: true, UserId: userId); } @@ -53,7 +53,7 @@ public async Task Ask( .Build(); try { - await app.SendActivityAsync(askActivity, cancellationToken); + await app.SendActivityAsync(askActivity, state.ServiceUrl, cancellationToken); } catch { @@ -139,7 +139,7 @@ public async Task RequestApproval( state.Approvals[approvalId] = ApprovalStatus.Pending; try { - await app.SendActivityAsync(activity, cancellationToken); + await app.SendActivityAsync(activity, state.ServiceUrl, cancellationToken); } catch { diff --git a/core/samples/Proactive/Worker.cs b/core/samples/Proactive/Worker.cs index b84a3dca..641d8723 100644 --- a/core/samples/Proactive/Worker.cs +++ b/core/samples/Proactive/Worker.cs @@ -19,12 +19,11 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) if (logger.IsEnabled(LogLevel.Information)) { CoreActivity proactiveMessage = CoreActivity.CreateBuilder() - .WithServiceUrl(new Uri(ServiceUrl)) .WithConversation(new(ConversationId)) .Build(); proactiveMessage.From = new ChannelAccount { Id = FromId }; proactiveMessage.Properties["text"] = $"Proactive hello at {DateTimeOffset.Now}"; - SendActivityResponse? aid = await conversationClient.SendActivityAsync(proactiveMessage, cancellationToken: stoppingToken); + SendActivityResponse? aid = await conversationClient.SendActivityAsync(proactiveMessage, new Uri(ServiceUrl), cancellationToken: stoppingToken); logger.LogInformation("Activity {Aid} sent", aid?.Id ?? "unknown"); } await Task.Delay(1000, stoppingToken); diff --git a/core/samples/TabApp/Program.cs b/core/samples/TabApp/Program.cs index a2d18ee4..52062b50 100644 --- a/core/samples/TabApp/Program.cs +++ b/core/samples/TabApp/Program.cs @@ -84,7 +84,7 @@ .WithServiceUrl(serviceUrl) .WithConversation(new TeamsConversation { Id = conversationId! }) .Build(); - await conversations.SendActivityAsync(activity, cancellationToken: ct); + await conversations.SendActivityAsync(activity, serviceUrl, cancellationToken: ct); return Results.Json(new PostToChatResult(Ok: true)); }).RequireAuthorization(); diff --git a/core/samples/TeamsBot/WelcomeMessageMiddleware.cs b/core/samples/TeamsBot/WelcomeMessageMiddleware.cs index 11fd64da..9514ff3c 100644 --- a/core/samples/TeamsBot/WelcomeMessageMiddleware.cs +++ b/core/samples/TeamsBot/WelcomeMessageMiddleware.cs @@ -45,7 +45,7 @@ public async Task OnTurnAsync(BotApplication botApplication, CoreActivity activi .WithConversationReference(TeamsActivity.FromActivity(activity)) .Build(); - await botApplication.SendActivityAsync(welcomeActivity, cancellationToken: cancellationToken); + await botApplication.SendActivityAsync(welcomeActivity, activity.ServiceUrl!, cancellationToken); _hasSentWelcomeMessage = true; } diff --git a/core/src/Microsoft.Teams.Apps.BotBuilder/CompatConversations.cs b/core/src/Microsoft.Teams.Apps.BotBuilder/CompatConversations.cs index 45fd1713..fee708b6 100644 --- a/core/src/Microsoft.Teams.Apps.BotBuilder/CompatConversations.cs +++ b/core/src/Microsoft.Teams.Apps.BotBuilder/CompatConversations.cs @@ -236,16 +236,10 @@ public async Task> ReplyToActivityWithHt CoreActivity coreActivity = activity.FromBotFrameworkActivity(); - // Default to the ServiceUrl from the adapter if it's not set on the activity, as ConversationClient requires it for sending activities - if (!string.IsNullOrWhiteSpace(ServiceUrl) && coreActivity.ServiceUrl == null) - { - coreActivity.ServiceUrl = new Uri(ServiceUrl); - } - coreActivity.ReplyToId = activityId; coreActivity.Conversation = new Microsoft.Teams.Core.Schema.Conversation(conversationId); - SendActivityResponse? response = await _client.SendActivityAsync(coreActivity, requestContext: RequestContext, customHeaders: convertedHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); + SendActivityResponse? response = await _client.SendActivityAsync(coreActivity, ResolveActivityServiceUrl(activity, "reply to activities"), requestContext: RequestContext, customHeaders: convertedHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); ResourceResponse resourceResponse = new() { @@ -307,15 +301,9 @@ public async Task> SendToConversationWit CoreActivity coreActivity = activity.FromBotFrameworkActivity(); - // Default to the ServiceUrl from the adapter if it's not set on the activity, as ConversationClient requires it for sending activities - if (!string.IsNullOrWhiteSpace(ServiceUrl) && coreActivity.ServiceUrl == null) - { - coreActivity.ServiceUrl = new Uri(ServiceUrl); - } - coreActivity.Conversation = new Microsoft.Teams.Core.Schema.Conversation(conversationId); - SendActivityResponse? response = await _client.SendActivityAsync(coreActivity, requestContext: RequestContext, customHeaders: convertedHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); + SendActivityResponse? response = await _client.SendActivityAsync(coreActivity, ResolveActivityServiceUrl(activity, "send activities"), requestContext: RequestContext, customHeaders: convertedHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); ResourceResponse resourceResponse = new() { @@ -347,16 +335,11 @@ public async Task> UpdateActivityWithHtt CoreActivity coreActivity = activity.FromBotFrameworkActivity(); - // Default to the ServiceUrl from the adapter if it's not set on the activity, as ConversationClient requires it for updating activities - if (!string.IsNullOrWhiteSpace(ServiceUrl) && coreActivity.ServiceUrl == null) - { - coreActivity.ServiceUrl = new Uri(ServiceUrl); - } - UpdateActivityResponse response = await _client.UpdateActivityAsync( conversationId, activityId, coreActivity, + ResolveActivityServiceUrl(activity, "update activities"), requestContext: RequestContext, customHeaders: convertedHeaders, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -422,6 +405,20 @@ public async Task> UploadAttachmentWithH return convertedHeaders; } + private Uri ResolveActivityServiceUrl(Activity activity, string operation) + { + string? serviceUrl = !string.IsNullOrWhiteSpace(activity.ServiceUrl) + ? activity.ServiceUrl + : ServiceUrl; + + if (string.IsNullOrWhiteSpace(serviceUrl)) + { + throw new InvalidOperationException($"ServiceUrl is required to {operation}."); + } + + return new Uri(serviceUrl); + } + public async Task> GetConversationMemberWithHttpMessagesAsync(string userId, string conversationId, Dictionary> customHeaders = null!, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(ServiceUrl); diff --git a/core/src/Microsoft.Teams.Apps.BotBuilder/TeamsBotAdapter.cs b/core/src/Microsoft.Teams.Apps.BotBuilder/TeamsBotAdapter.cs index f8326375..b789a397 100644 --- a/core/src/Microsoft.Teams.Apps.BotBuilder/TeamsBotAdapter.cs +++ b/core/src/Microsoft.Teams.Apps.BotBuilder/TeamsBotAdapter.cs @@ -105,17 +105,21 @@ public override async Task SendActivitiesAsync(ITurnContext CoreActivity coreActivity = activity.FromBotFrameworkActivity(); - // Ensure ServiceUrl is set from turn context if not already present - if (coreActivity.ServiceUrl == null && !string.IsNullOrWhiteSpace(inboundActivity.ServiceUrl)) + string? serviceUrlString = !string.IsNullOrWhiteSpace(activity.ServiceUrl) + ? activity.ServiceUrl + : inboundActivity.ServiceUrl; + if (string.IsNullOrWhiteSpace(serviceUrlString)) { - coreActivity.ServiceUrl = new Uri(inboundActivity.ServiceUrl); + throw new InvalidOperationException("ServiceUrl is required to send activities."); } + Uri serviceUrl = new(serviceUrlString); coreActivity.Conversation ??= new Microsoft.Teams.Core.Schema.Conversation( inboundActivity.Conversation?.Id ?? throw new InvalidOperationException("Conversation ID is required to send activities.")); SendActivityResponse? resp = await botApplication.ConversationClient.SendActivityAsync( coreActivity, + serviceUrl, requestContext, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -143,16 +147,20 @@ public override async Task UpdateActivityAsync(ITurnContext tu CoreActivity coreActivity = activity.FromBotFrameworkActivity(); - // Ensure ServiceUrl is set from turn context if not already present - if (coreActivity.ServiceUrl == null && !string.IsNullOrWhiteSpace(turnContext.Activity.ServiceUrl)) + string? serviceUrlString = !string.IsNullOrWhiteSpace(activity.ServiceUrl) + ? activity.ServiceUrl + : turnContext.Activity.ServiceUrl; + if (string.IsNullOrWhiteSpace(serviceUrlString)) { - coreActivity.ServiceUrl = new Uri(turnContext.Activity.ServiceUrl); + throw new InvalidOperationException("ServiceUrl is required to update activities."); } + Uri serviceUrl = new(serviceUrlString); UpdateActivityResponse res = await botApplication.ConversationClient.UpdateActivityAsync( activity.Conversation.Id, activity.Id, coreActivity, + serviceUrl, requestContext: BotRequestContext.FromInboundActivity(turnContext.Activity?.FromBotFrameworkActivity()), cancellationToken: cancellationToken).ConfigureAwait(false); return new ResourceResponse() { Id = res.Id }; diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs index 1cf236fa..a8ed2769 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ActivityClient.cs @@ -32,9 +32,8 @@ internal ActivityClient(Uri serviceUrl, CoreConversationClient client) public Task CreateAsync(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); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -43,8 +42,7 @@ internal ActivityClient(Uri serviceUrl, CoreConversationClient client) public Task UpdateAsync(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); + return _client.UpdateActivityAsync(conversationId, id, activity, _serviceUrl, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -54,9 +52,8 @@ public Task UpdateAsync(string conversationId, string id { ArgumentNullException.ThrowIfNull(activity); activity.ReplyToId = id; - activity.ServiceUrl ??= _serviceUrl; activity.Conversation ??= new Conversation(conversationId); - return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -75,14 +72,13 @@ public Task DeleteAsync(string conversationId, string id, AgenticIdentity? agent public Task CreateTargetedAsync(string conversationId, CoreActivity activity, Dictionary? additionalHeaders = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(activity); - activity.ServiceUrl ??= _serviceUrl; activity.Conversation ??= new Conversation(conversationId); // Ensure recipient is marked as targeted if (activity.Recipient is not null) { activity.Recipient.IsTargeted = true; } - return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -92,8 +88,7 @@ public Task DeleteAsync(string conversationId, string id, AgenticIdentity? agent public Task UpdateTargetedAsync(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); + return _client.UpdateTargetedActivityAsync(conversationId, id, activity, _serviceUrl, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs index 26b49bd0..1ae190c2 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ConversationApiClient.cs @@ -66,9 +66,8 @@ public Task CreateAsync(ConversationParameters reque 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); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -77,8 +76,7 @@ public Task CreateAsync(ConversationParameters reque 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); + return _client.UpdateActivityAsync(conversationId, id, activity, _serviceUrl, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -88,9 +86,8 @@ public Task UpdateActivityAsync(string conversationId, s { ArgumentNullException.ThrowIfNull(activity); activity.ReplyToId = id; - activity.ServiceUrl ??= _serviceUrl; activity.Conversation ??= new Conversation(conversationId); - return _client.SendActivityAsync(activity, customHeaders: additionalHeaders, cancellationToken: cancellationToken); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -109,13 +106,12 @@ public Task DeleteActivityAsync(string conversationId, string id, AgenticIdentit 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); + return _client.SendActivityAsync(activity, _serviceUrl, customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// @@ -125,8 +121,7 @@ public Task DeleteActivityAsync(string conversationId, string id, AgenticIdentit 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); + return _client.UpdateTargetedActivityAsync(conversationId, id, activity, _serviceUrl, requestContext: BotRequestContext.FromActivity(activity), customHeaders: additionalHeaders, cancellationToken: cancellationToken); } /// diff --git a/core/src/Microsoft.Teams.Apps/Context.cs b/core/src/Microsoft.Teams.Apps/Context.cs index 53851f38..88708a4f 100644 --- a/core/src/Microsoft.Teams.Apps/Context.cs +++ b/core/src/Microsoft.Teams.Apps/Context.cs @@ -29,6 +29,9 @@ public class Context(TeamsBotApplication botApplication, TActivity ac /// public TActivity Activity { get; } = activity; + private Uri ServiceUrl => Activity.ServiceUrl + ?? throw new InvalidOperationException("Activity.ServiceUrl is required."); + /// /// Gets the application (client) ID configured for this bot. /// @@ -48,8 +51,7 @@ public class Context(TeamsBotApplication botApplication, TActivity ac /// /// Gets the scoped to the current activity's service URL. /// - public ApiClient Api => _api ??= TeamsBotApplication.Api.ForServiceUrl( - Activity.ServiceUrl ?? throw new InvalidOperationException("Activity.ServiceUrl is required to use the Api client.")); + public ApiClient Api => _api ??= TeamsBotApplication.Api.ForServiceUrl(ServiceUrl); // ==================== Turn State ==================== @@ -260,7 +262,7 @@ internal Context CreateDerivedContext(TNew activity) where TNew : Te TeamsActivity reply = new TeamsActivityBuilder(activity) .WithConversationReference(Activity) .Build(); - return TeamsBotApplication.SendActivityAsync(reply, cancellationToken: cancellationToken); + return TeamsBotApplication.SendActivityAsync(reply, ServiceUrl, cancellationToken: cancellationToken); } /// @@ -274,7 +276,7 @@ internal Context CreateDerivedContext(TNew activity) where TNew : Te .WithType(TeamsActivityTypes.Typing) .WithConversationReference(Activity) .Build(); - return TeamsBotApplication.SendActivityAsync(reply, cancellationToken: cancellationToken); + return TeamsBotApplication.SendActivityAsync(reply, ServiceUrl, cancellationToken: cancellationToken); } // ==================== OAuth Sign-In ==================== diff --git a/core/src/Microsoft.Teams.Apps/Schema/TeamsActivityBuilder.cs b/core/src/Microsoft.Teams.Apps/Schema/TeamsActivityBuilder.cs index 999e2046..9d51b90e 100644 --- a/core/src/Microsoft.Teams.Apps/Schema/TeamsActivityBuilder.cs +++ b/core/src/Microsoft.Teams.Apps/Schema/TeamsActivityBuilder.cs @@ -27,6 +27,28 @@ internal TeamsActivityBuilder(TeamsActivity activity) : base(activity) { } + /// + /// Sets the service URL. + /// + /// The service URL. + /// The builder instance for chaining. + public TeamsActivityBuilder WithServiceUrl(Uri? serviceUrl) + { + _activity.ServiceUrl = serviceUrl; + return this; + } + + /// + /// Sets the service URL from a string. + /// + /// The service URL as a string. + /// The builder instance for chaining. + public TeamsActivityBuilder WithServiceUrl(string serviceUrlString) + { + _activity.ServiceUrl = new Uri(serviceUrlString); + return this; + } + /// /// Apply Conversation Reference from the specified activity. /// diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs index f273c8e9..ab5dc28f 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs @@ -222,7 +222,7 @@ public TeamsBotApplication( TeamsActivity activity = builder.Build(); - return SendActivityAsync(activity, cancellationToken: cancellationToken); + return SendActivityAsync(activity, resolvedUrl, cancellationToken: cancellationToken); } /// diff --git a/core/src/Microsoft.Teams.Apps/TeamsStreamingWriter.cs b/core/src/Microsoft.Teams.Apps/TeamsStreamingWriter.cs index a5b35729..5d6b3611 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsStreamingWriter.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsStreamingWriter.cs @@ -54,6 +54,7 @@ public sealed class TeamsStreamingWriter private readonly ConversationClient _client; private readonly TeamsActivity _reference; private readonly string _conversationId; + private readonly Uri _serviceUrl; private readonly ILogger _logger; // Assigned from the server's 201 response after the first send; null until then. private string? _streamId; @@ -80,6 +81,7 @@ internal TeamsStreamingWriter(ConversationClient client, TeamsActivity reference _client = client; _reference = reference; _conversationId = reference.Conversation?.Id ?? throw new ArgumentException("Activity must have a Conversation with an Id.", nameof(reference)); + _serviceUrl = reference.ServiceUrl ?? throw new ArgumentException("Activity must have a ServiceUrl.", nameof(reference)); _logger = logger ?? NullLogger.Instance; } @@ -218,7 +220,7 @@ public async Task FinalizeResponseAsync(MessageActivity? final = null, Cancellat try { - await _client.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false); + await _client.SendActivityAsync(activity, _serviceUrl, cancellationToken: cancellationToken).ConfigureAwait(false); } catch (HttpRequestException ex) { @@ -255,7 +257,7 @@ public async Task FinalizeResponseAsync(MessageActivity? final = null, Cancellat { try { - return await _client.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false); + return await _client.SendActivityAsync(activity, _serviceUrl, cancellationToken: cancellationToken).ConfigureAwait(false); } catch (HttpRequestException ex) { @@ -348,12 +350,12 @@ private async Task SendFinalInPlaceAsync(MessageActivity final, CancellationToke if (_streamId != null) { _logger.LogDebug("Updating original streamed message in place after timeout (streamId '{StreamId}').", _streamId); - await _client.UpdateActivityAsync(_conversationId, _streamId, activity, cancellationToken: cancellationToken).ConfigureAwait(false); + await _client.UpdateActivityAsync(_conversationId, _streamId, activity, _serviceUrl, cancellationToken: cancellationToken).ConfigureAwait(false); } else { // No streamed message exists yet; send the buffered content as a normal message. - await _client.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false); + await _client.SendActivityAsync(activity, _serviceUrl, cancellationToken: cancellationToken).ConfigureAwait(false); } } diff --git a/core/src/Microsoft.Teams.Core/BotApplication.cs b/core/src/Microsoft.Teams.Core/BotApplication.cs index 1a4d16ef..48bbea34 100644 --- a/core/src/Microsoft.Teams.Core/BotApplication.cs +++ b/core/src/Microsoft.Teams.Core/BotApplication.cs @@ -35,10 +35,10 @@ namespace Microsoft.Teams.Core; /// CoreActivity.CreateBuilder() /// .WithType(ActivityType.Message) /// .WithConversation(activity.Conversation) -/// .WithServiceUrl(activity.ServiceUrl) -/// .WithProperty("text", "Hello!") -/// .Build(), -/// ct); +/// .WithProperty("text", "Hello!") +/// .Build(), +/// activity.ServiceUrl!, +/// ct); /// }; /// /// app.Run(); @@ -64,9 +64,9 @@ namespace Microsoft.Teams.Core; /// CoreActivity.CreateBuilder() /// .WithType(ActivityType.Message) /// .WithConversation(activity.Conversation) -/// .WithServiceUrl(activity.ServiceUrl) /// .WithProperty("text", $"You said: {activity.Properties["text"]}") /// .Build(), +/// activity.ServiceUrl!, /// ct); /// } /// } @@ -153,9 +153,9 @@ public BotApplication(ConversationClient conversationClient, UserTokenClient use /// CoreActivity.CreateBuilder() /// .WithType(ActivityType.Message) /// .WithConversation(activity.Conversation) - /// .WithServiceUrl(activity.ServiceUrl) /// .WithProperty("text", "Received your message!") /// .Build(), + /// activity.ServiceUrl!, /// ct); /// } /// }; @@ -287,31 +287,32 @@ public ITurnMiddleware UseMiddleware(ITurnMiddleware middleware) /// /// /// This is a convenience wrapper around . The activity - /// must have its and properties set. + /// must have its property set. /// /// /// var reply = CoreActivity.CreateBuilder() /// .WithType(ActivityType.Message) /// .WithConversation(incomingActivity.Conversation) - /// .WithServiceUrl(incomingActivity.ServiceUrl) /// .WithProperty("text", "Hello from the bot!") /// .Build(); /// - /// SendActivityResponse? response = await bot.SendActivityAsync(reply, cancellationToken); + /// SendActivityResponse? response = await bot.SendActivityAsync(reply, incomingActivity.ServiceUrl!, cancellationToken); /// string? sentId = response?.Id; /// /// /// - /// The activity to send. Cannot be null. Must have and set. + /// The activity to send. Cannot be null. Must have set. + /// The service URL for the conversation. Cannot be null. /// A cancellation token that can be used to cancel the send operation. /// A task that represents the asynchronous operation. The task result contains a with the ID of the sent activity, or null. /// Thrown if is null or the conversation client has not been initialized. - public async Task SendActivityAsync(CoreActivity activity, CancellationToken cancellationToken = default) + public async Task SendActivityAsync(CoreActivity activity, Uri serviceUrl, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(activity); + ArgumentNullException.ThrowIfNull(serviceUrl); ArgumentNullException.ThrowIfNull(_conversationClient, "ConversationClient not initialized"); - return await _conversationClient.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false); + return await _conversationClient.SendActivityAsync(activity, serviceUrl, cancellationToken: cancellationToken).ConfigureAwait(false); } /// diff --git a/core/src/Microsoft.Teams.Core/ConversationClient.cs b/core/src/Microsoft.Teams.Core/ConversationClient.cs index 39554551..0ae71218 100644 --- a/core/src/Microsoft.Teams.Core/ConversationClient.cs +++ b/core/src/Microsoft.Teams.Core/ConversationClient.cs @@ -39,20 +39,21 @@ public class ConversationClient(HttpClient httpClient, ILogger /// Sends the specified activity to the conversation endpoint asynchronously. /// - /// The activity to send. Cannot be null. Must contain a valid ServiceUrl and Conversation with an Id. + /// The activity to send. Cannot be null. Must contain a Conversation with an Id. /// The recipient's IsTargeted property determines if this is a targeted activity. + /// The service URL for the conversation. Cannot be null. /// Optional per-request properties (see ) used as a fallback; values derived from the activity take precedence. /// Optional custom headers to include in the request. /// A cancellation token that can be used to cancel the send operation. /// A task that represents the asynchronous operation. The task result contains the response with the ID of the sent activity. /// Thrown if the activity could not be sent successfully. The exception message includes the HTTP status code and /// response content. - public virtual async Task SendActivityAsync(CoreActivity activity, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) + public virtual async Task SendActivityAsync(CoreActivity activity, Uri serviceUrl, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(activity); + ArgumentNullException.ThrowIfNull(serviceUrl); string? conversationId = activity.Conversation?.Id; ArgumentException.ThrowIfNullOrWhiteSpace(conversationId); - ArgumentNullException.ThrowIfNull(activity.ServiceUrl); #pragma warning disable ExperimentalTeamsTargeted bool isTargeted = activity.Recipient?.IsTargeted == true; @@ -60,13 +61,13 @@ public class ConversationClient(HttpClient httpClient, ILogger 100 ? conversationId[..100] : conversationId; - url = $"{activity.ServiceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(convId)}/activities/"; + url = $"{serviceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(convId)}/activities/"; } if (isTargeted) @@ -81,7 +82,7 @@ public class ConversationClient(HttpClient httpClient, ILoggerThe ID of the conversation. Cannot be null or whitespace. /// The ID of the activity to update. Cannot be null or whitespace. /// The updated activity data. Cannot be null. + /// The service URL for the conversation. Cannot be null. /// Whether this is a targeted activity visible only to a specific recipient. /// Optional per-request properties (see ) to stamp onto the request's options. /// Optional custom headers to include in the request. /// A cancellation token that can be used to cancel the update operation. /// A task that represents the asynchronous operation. The task result contains the response with the ID of the updated activity. /// Thrown if the activity could not be updated successfully. - public virtual async Task UpdateActivityAsync(string conversationId, string activityId, CoreActivity activity, bool isTargeted = false, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) + public virtual async Task UpdateActivityAsync(string conversationId, string activityId, CoreActivity activity, Uri serviceUrl, bool isTargeted = false, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(conversationId); ArgumentException.ThrowIfNullOrWhiteSpace(activityId); ArgumentNullException.ThrowIfNull(activity); - ArgumentNullException.ThrowIfNull(activity.ServiceUrl); + ArgumentNullException.ThrowIfNull(serviceUrl); - string url = $"{activity.ServiceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(conversationId)}/activities/{Uri.EscapeDataString(activityId)}"; + string url = $"{serviceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(conversationId)}/activities/{Uri.EscapeDataString(activityId)}"; if (isTargeted) { @@ -140,7 +142,7 @@ public virtual async Task UpdateActivityAsync(string con if (span is not null) { span.SetTag(Telemetry.Tags.Operation, Telemetry.Operations.UpdateActivity); - span.SetTag(Telemetry.Tags.ServiceUrl, activity.ServiceUrl.ToString()); + span.SetTag(Telemetry.Tags.ServiceUrl, serviceUrl.ToString()); span.SetTag(Telemetry.Tags.ConversationId, conversationId); span.SetTag(Telemetry.Tags.ActivityId, activityId); span.SetTag(Telemetry.Tags.ActivityType, activity.Type); @@ -171,20 +173,21 @@ public virtual async Task UpdateActivityAsync(string con /// /// The ID of the conversation. Cannot be null or whitespace. /// The ID of the activity to update. Cannot be null or whitespace. - /// The updated activity data. Cannot be null. Must contain a valid ServiceUrl. + /// The updated activity data. Cannot be null. + /// The service URL for the conversation. Cannot be null. /// Optional per-request properties (see ) to stamp onto the request's options. /// Optional custom headers to include in the request. /// A cancellation token that can be used to cancel the update operation. /// A task that represents the asynchronous operation. The task result contains the response with the ID of the updated activity. /// Thrown if the activity could not be updated successfully. - public virtual async Task UpdateTargetedActivityAsync(string conversationId, string activityId, CoreActivity activity, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) + public virtual async Task UpdateTargetedActivityAsync(string conversationId, string activityId, CoreActivity activity, Uri serviceUrl, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(conversationId); ArgumentException.ThrowIfNullOrWhiteSpace(activityId); ArgumentNullException.ThrowIfNull(activity); - ArgumentNullException.ThrowIfNull(activity.ServiceUrl); + ArgumentNullException.ThrowIfNull(serviceUrl); - string url = $"{activity.ServiceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(conversationId)}/activities/{Uri.EscapeDataString(activityId)}?isTargetedActivity=true"; + string url = $"{serviceUrl.ToString().TrimEnd('/')}/v3/conversations/{Uri.EscapeDataString(conversationId)}/activities/{Uri.EscapeDataString(activityId)}?isTargetedActivity=true"; string body = activity.ToJson(); @@ -195,7 +198,7 @@ public virtual async Task UpdateTargetedActivityAsync(st if (span is not null) { span.SetTag(Telemetry.Tags.Operation, Telemetry.Operations.UpdateActivity); - span.SetTag(Telemetry.Tags.ServiceUrl, activity.ServiceUrl.ToString()); + span.SetTag(Telemetry.Tags.ServiceUrl, serviceUrl.ToString()); span.SetTag(Telemetry.Tags.ConversationId, conversationId); span.SetTag(Telemetry.Tags.ActivityId, activityId); span.SetTag(Telemetry.Tags.ActivityType, activity.Type); @@ -303,24 +306,25 @@ await _botHttpClient.SendAsync( /// Deletes an existing activity from a conversation using activity context. /// /// The ID of the conversation. - /// The activity to delete. Must contain valid Id and ServiceUrl. Cannot be null. + /// The activity to delete. Must contain a valid Id. Cannot be null. + /// The service URL for the conversation. Cannot be null. /// Whether this is a targeted activity. /// Optional per-request properties (see ) to stamp onto the request's options. /// Optional custom headers to include in the request. /// A cancellation token that can be used to cancel the delete operation. /// A task that represents the asynchronous operation. /// Thrown if the activity could not be deleted successfully. - public virtual async Task DeleteActivityAsync(string conversationId, CoreActivity activity, bool isTargeted = false, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) + public virtual async Task DeleteActivityAsync(string conversationId, CoreActivity activity, Uri serviceUrl, bool isTargeted = false, BotRequestContext? requestContext = null, CustomHeaders? customHeaders = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(activity); + ArgumentNullException.ThrowIfNull(serviceUrl); ArgumentException.ThrowIfNullOrWhiteSpace(activity.Id); ArgumentException.ThrowIfNullOrWhiteSpace(conversationId); - ArgumentNullException.ThrowIfNull(activity.ServiceUrl); await DeleteActivityAsync( conversationId, activity.Id, - activity.ServiceUrl, + serviceUrl, isTargeted, requestContext, customHeaders, diff --git a/core/src/Microsoft.Teams.Core/Schema/CoreActivityBuilder.cs b/core/src/Microsoft.Teams.Core/Schema/CoreActivityBuilder.cs index e4a7c08c..1ebf5760 100644 --- a/core/src/Microsoft.Teams.Core/Schema/CoreActivityBuilder.cs +++ b/core/src/Microsoft.Teams.Core/Schema/CoreActivityBuilder.cs @@ -40,27 +40,6 @@ public TBuilder WithId(string id) return (TBuilder)this; } - /// - /// Sets the service URL. - /// - /// The service URL. - /// The builder instance for chaining. - public TBuilder WithServiceUrl(Uri? serviceUrl) - { - _activity.ServiceUrl = serviceUrl; - return (TBuilder)this; - } - /// - /// Sets the service URL from a string. - /// - /// The service URL as a string. - /// The builder instance for chaining. - public TBuilder WithServiceUrl(string serviceUrlString) - { - _activity.ServiceUrl = new Uri(serviceUrlString); - return (TBuilder)this; - } - /// /// Sets the channel ID. /// diff --git a/core/test/IntegrationTests/ConversationClientTests.cs b/core/test/IntegrationTests/ConversationClientTests.cs index 35324bcf..e738681d 100644 --- a/core/test/IntegrationTests/ConversationClientTests.cs +++ b/core/test/IntegrationTests/ConversationClientTests.cs @@ -30,12 +30,11 @@ public async Task SendActivity() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(_f.ConversationId)) .WithProperty("text", $"[ConversationClient] SendActivity at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? res = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? res = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(res); Assert.NotNull(res.Id); @@ -49,24 +48,22 @@ public async Task UpdateActivity() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(_f.ConversationId)) .WithProperty("text", $"[ConversationClient] Original at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(sent?.Id); CoreActivity updated = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(_f.ConversationId)) .WithProperty("text", $"[ConversationClient] Updated at `{DateTime.UtcNow:s}`") .Build(); UpdateActivityResponse res = await _f.ConversationClient.UpdateActivityAsync( - _f.ConversationId, sent.Id, updated, false, BotRequestContext.FromAgenticIdentity(_f.AgenticIdentity)); + _f.ConversationId, sent.Id, updated, _f.ServiceUrl, false, BotRequestContext.FromAgenticIdentity(_f.AgenticIdentity)); Assert.NotNull(res?.Id); _output.WriteLine($"Updated activity: {res.Id}"); @@ -79,12 +76,11 @@ public async Task DeleteActivity() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(_f.ConversationId)) .WithProperty("text", $"[ConversationClient] To delete at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(sent?.Id); await Task.Delay(2000); @@ -153,13 +149,12 @@ public async Task AddAndDeleteReaction() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) - .WithServiceUrl(_f.ServiceUrl) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) .WithConversation(new(_f.ConversationId)) .WithProperty("text", $"[ConversationClient] Reaction test at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(sent?.Id); await _f.ConversationClient.AddReactionAsync( diff --git a/core/test/IntegrationTests/CreateConversationTests.cs b/core/test/IntegrationTests/CreateConversationTests.cs index 88dc1bbf..1e155057 100644 --- a/core/test/IntegrationTests/CreateConversationTests.cs +++ b/core/test/IntegrationTests/CreateConversationTests.cs @@ -85,12 +85,11 @@ public async Task Core_CreatePersonalChat_AndSendMessage() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(response.Id)) .WithProperty("text", $"[Core] 1:1 message at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(sent?.Id); _output.WriteLine($"Created 1:1 conversation {response.Id} and sent activity {sent.Id}"); } @@ -189,12 +188,11 @@ public async Task Core_CreateGroupChat_AndSendMessage() CoreActivity activity = CoreActivity.CreateBuilder() .WithType(ActivityType.Message) .WithFrom(IntegrationTestFixture.GetChannelAccountWithAgenticProperties()) - .WithServiceUrl(_f.ServiceUrl) .WithConversation(new(response.Id)) .WithProperty("text", $"[Core] Group message at `{DateTime.UtcNow:s}`") .Build(); - SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity); + SendActivityResponse? sent = await _f.ConversationClient.SendActivityAsync(activity, _f.ServiceUrl); Assert.NotNull(sent?.Id); _output.WriteLine($"Created group {response.Id} and sent activity {sent.Id}"); } diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs index 43781227..2956aeac 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs @@ -146,12 +146,13 @@ await Assert.ThrowsAsync( } [Fact] - public async Task SendActivitiesAsync_SetsServiceUrlFromTurnContext() + public async Task SendActivitiesAsync_PassesServiceUrlFromTurnContext() { // Arrange Mock mockConversationClient = CreateMockConversationClient(); mockConversationClient.Setup(c => c.SendActivityAsync( It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) @@ -178,7 +179,8 @@ public async Task SendActivitiesAsync_SetsServiceUrlFromTurnContext() mockConversationClient.Verify( c => c.SendActivityAsync( - It.Is(a => a.ServiceUrl != null && a.ServiceUrl.ToString().TrimEnd('/') == "https://turn-context-service-url.com"), + It.Is(a => a.ServiceUrl == null), + It.Is(u => u.ToString().TrimEnd('/') == "https://turn-context-service-url.com"), It.Is(c => c != null && c.BotAppId == "bot-123"), It.IsAny?>(), It.IsAny()), @@ -186,7 +188,7 @@ public async Task SendActivitiesAsync_SetsServiceUrlFromTurnContext() } [Fact] - public async Task UpdateActivityAsync_SetsServiceUrlFromTurnContext() + public async Task UpdateActivityAsync_PassesServiceUrlFromTurnContext() { // Arrange Mock mockConversationClient = CreateMockConversationClient(); @@ -194,6 +196,7 @@ public async Task UpdateActivityAsync_SetsServiceUrlFromTurnContext() It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), @@ -223,7 +226,8 @@ public async Task UpdateActivityAsync_SetsServiceUrlFromTurnContext() c => c.UpdateActivityAsync( "conversation-123", "activity-123", - It.Is(a => a.ServiceUrl != null && a.ServiceUrl.ToString().TrimEnd('/') == "https://turn-context-service-url.com"), + It.Is(a => a.ServiceUrl == null), + It.Is(u => u.ToString().TrimEnd('/') == "https://turn-context-service-url.com"), It.IsAny(), It.IsAny(), It.IsAny?>(), @@ -248,6 +252,7 @@ private static Mock CreateMockConversationClient() mock.Setup(c => c.SendActivityAsync( It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatConversationsTests.cs b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatConversationsTests.cs index 1abdcadd..5bdbcf86 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatConversationsTests.cs +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatConversationsTests.cs @@ -17,7 +17,7 @@ public class CompatConversationsTests private const string TestActivityId = "test-activity-id"; [Fact] - public async Task SendToConversationWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull() + public async Task SendToConversationWithHttpMessagesAsync_PassesServiceUrlFromProperty_WhenActivityServiceUrlIsNull() { // Arrange Mock mockConversationClient = CreateMockConversationClient(); @@ -33,9 +33,14 @@ public async Task SendToConversationWithHttpMessagesAsync_SetsServiceUrlFromProp }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, serviceUrl, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -43,15 +48,16 @@ public async Task SendToConversationWithHttpMessagesAsync_SetsServiceUrlFromProp // Assert Assert.NotNull(capturedActivity); - Assert.NotNull(capturedActivity.ServiceUrl); - Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.Null(capturedActivity.ServiceUrl); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); mockConversationClient.Verify( - c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), + c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), Times.Once); } [Fact] - public async Task SendToConversationWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet() + public async Task SendToConversationWithHttpMessagesAsync_UsesActivityServiceUrl_WhenActivityServiceUrlIsSet() { // Arrange const string activityServiceUrl = "https://custom.service.url/"; @@ -69,9 +75,14 @@ public async Task SendToConversationWithHttpMessagesAsync_DoesNotOverrideService }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, serviceUrl, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -81,13 +92,15 @@ public async Task SendToConversationWithHttpMessagesAsync_DoesNotOverrideService Assert.NotNull(capturedActivity); Assert.NotNull(capturedActivity.ServiceUrl); Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); mockConversationClient.Verify( - c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), + c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), Times.Once); } [Fact] - public async Task ReplyToActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull() + public async Task ReplyToActivityWithHttpMessagesAsync_PassesServiceUrlFromProperty_WhenActivityServiceUrlIsNull() { // Arrange Mock mockConversationClient = CreateMockConversationClient(); @@ -103,9 +116,14 @@ public async Task ReplyToActivityWithHttpMessagesAsync_SetsServiceUrlFromPropert }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, serviceUrl, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -113,16 +131,17 @@ public async Task ReplyToActivityWithHttpMessagesAsync_SetsServiceUrlFromPropert // Assert Assert.NotNull(capturedActivity); - Assert.NotNull(capturedActivity.ServiceUrl); - Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.Null(capturedActivity.ServiceUrl); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); Assert.Equal(TestActivityId, capturedActivity.ReplyToId); mockConversationClient.Verify( - c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), + c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), Times.Once); } [Fact] - public async Task ReplyToActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet() + public async Task ReplyToActivityWithHttpMessagesAsync_UsesActivityServiceUrl_WhenActivityServiceUrlIsSet() { // Arrange const string activityServiceUrl = "https://custom.service.url/"; @@ -140,9 +159,14 @@ public async Task ReplyToActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, serviceUrl, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -152,13 +176,15 @@ public async Task ReplyToActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl Assert.NotNull(capturedActivity); Assert.NotNull(capturedActivity.ServiceUrl); Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); mockConversationClient.Verify( - c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), + c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny()), Times.Once); } [Fact] - public async Task UpdateActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull() + public async Task UpdateActivityWithHttpMessagesAsync_PassesServiceUrlFromProperty_WhenActivityServiceUrlIsNull() { // Arrange Mock mockConversationClient = CreateMockConversationClient(); @@ -174,16 +200,22 @@ public async Task UpdateActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient .Setup(c => c.UpdateActivityAsync( It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((_, _, act, _, _, _, _) => capturedActivity = act) + .Callback?, CancellationToken>((_, _, act, serviceUrl, _, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId }); // Act @@ -191,13 +223,15 @@ public async Task UpdateActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty // Assert Assert.NotNull(capturedActivity); - Assert.NotNull(capturedActivity.ServiceUrl); - Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.Null(capturedActivity.ServiceUrl); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); mockConversationClient.Verify( c => c.UpdateActivityAsync( TestConversationId, TestActivityId, It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), @@ -230,6 +264,7 @@ public async Task UpdateActivityWithHttpMessagesAsync_PassesRequestContext() TestConversationId, TestActivityId, It.IsAny(), + It.IsAny(), It.IsAny(), It.Is(c => ReferenceEquals(c, requestContext)), It.IsAny?>(), @@ -244,7 +279,7 @@ public async Task UpdateActivityWithHttpMessagesAsync_PassesRequestContext() } [Fact] - public async Task UpdateActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet() + public async Task UpdateActivityWithHttpMessagesAsync_UsesActivityServiceUrl_WhenActivityServiceUrlIsSet() { // Arrange const string activityServiceUrl = "https://custom.service.url/"; @@ -262,16 +297,22 @@ public async Task UpdateActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_ }; CoreActivity? capturedActivity = null; + Uri? capturedServiceUrl = null; mockConversationClient .Setup(c => c.UpdateActivityAsync( It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((_, _, act, _, _, _, _) => capturedActivity = act) + .Callback?, CancellationToken>((_, _, act, serviceUrl, _, _, _, _) => + { + capturedActivity = act; + capturedServiceUrl = serviceUrl; + }) .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId }); // Act @@ -281,11 +322,14 @@ public async Task UpdateActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_ Assert.NotNull(capturedActivity); Assert.NotNull(capturedActivity.ServiceUrl); Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/')); + Assert.NotNull(capturedServiceUrl); + Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedServiceUrl.ToString().TrimEnd('/')); mockConversationClient.Verify( c => c.UpdateActivityAsync( TestConversationId, TestActivityId, It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), @@ -311,8 +355,8 @@ public async Task SendToConversationWithHttpMessagesAsync_EnsuresConversationIdI CoreActivity? capturedActivity = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, _, _, _, _) => capturedActivity = act) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -341,8 +385,8 @@ public async Task ReplyToActivityWithHttpMessagesAsync_SetsReplyToIdProperty() CoreActivity? capturedActivity = null; mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>((act, _, _, _) => capturedActivity = act) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Callback?, CancellationToken>((act, _, _, _, _) => capturedActivity = act) .ReturnsAsync(new SendActivityResponse { Id = TestActivityId }); // Act @@ -365,7 +409,7 @@ public async Task SendToConversationWithHttpMessagesAsync_WhenSendActivityReturn // Arrange Mock mockConversationClient = CreateMockConversationClient(); mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body CompatConversations compatConversations = new(mockConversationClient.Object) @@ -398,7 +442,7 @@ public async Task ReplyToActivityWithHttpMessagesAsync_WhenSendActivityReturnsNu // Arrange Mock mockConversationClient = CreateMockConversationClient(); mockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body CompatConversations compatConversations = new(mockConversationClient.Object) diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTelemetryTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTelemetryTests.cs index 64c4932b..aa14992e 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTelemetryTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTelemetryTests.cs @@ -492,7 +492,7 @@ private static void SetupGetSignInResource(Mock mock) private static void SetupSendActivity(TestHarness harness) { harness.MockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) .ReturnsAsync(new SendActivityResponse { Id = "activity-1" }); } diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs index 97b526b2..e97ef108 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs @@ -555,7 +555,7 @@ private static void SetupGetSignInResource(Mock mock) private static void SetupSendActivity(TestHarness harness) { harness.MockConversationClient - .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Setup(c => c.SendActivityAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) .ReturnsAsync(new SendActivityResponse { Id = "activity-1" }); } } diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs index 2878f099..c05c5d03 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs @@ -190,11 +190,12 @@ private static CaptureSlot SetupCapture(TestHarness harness) harness.MockConversationClient .Setup(c => c.SendActivityAsync( It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny?>(), It.IsAny())) - .Callback?, CancellationToken>( - (activity, _, _, _) => slot.Value = activity) + .Callback?, CancellationToken>( + (activity, _, _, _, _) => slot.Value = activity) .ReturnsAsync(new SendActivityResponse { Id = "sent-id" }); return slot; } diff --git a/core/test/Microsoft.Teams.Core.UnitTests/BotApplicationTests.cs b/core/test/Microsoft.Teams.Core.UnitTests/BotApplicationTests.cs index 53075958..aff2b641 100644 --- a/core/test/Microsoft.Teams.Core.UnitTests/BotApplicationTests.cs +++ b/core/test/Microsoft.Teams.Core.UnitTests/BotApplicationTests.cs @@ -171,7 +171,7 @@ public async Task SendActivityAsync_WithValidActivity_SendsSuccessfully() ServiceUrl = new Uri("https://test.service.url/"), Conversation = new("conv123") }; - SendActivityResponse? result = await botApp.SendActivityAsync(activity); + SendActivityResponse? result = await botApp.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(result); Assert.Contains("activity123", result.Id); @@ -183,7 +183,7 @@ public async Task SendActivityAsync_WithNullActivity_ThrowsArgumentNullException BotApplication botApp = CreateBotApplication(); await Assert.ThrowsAsync(() => - botApp.SendActivityAsync(null!)); + botApp.SendActivityAsync(null!, new Uri("https://test.service.url/"))); } [Fact] diff --git a/core/test/Microsoft.Teams.Core.UnitTests/ConversationClientTests.cs b/core/test/Microsoft.Teams.Core.UnitTests/ConversationClientTests.cs index 4b089ca1..71ad3ab9 100644 --- a/core/test/Microsoft.Teams.Core.UnitTests/ConversationClientTests.cs +++ b/core/test/Microsoft.Teams.Core.UnitTests/ConversationClientTests.cs @@ -37,7 +37,7 @@ public async Task SendActivityAsync_WithValidActivity_SendsSuccessfully() Conversation = new("conv123") }; - SendActivityResponse? result = await conversationClient.SendActivityAsync(activity); + SendActivityResponse? result = await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(result); Assert.Contains("activity123", result.Id); @@ -50,7 +50,7 @@ public async Task SendActivityAsync_WithNullActivity_ThrowsArgumentNullException ConversationClient conversationClient = new(httpClient); await Assert.ThrowsAsync(() => - conversationClient.SendActivityAsync(null!)); + conversationClient.SendActivityAsync(null!, new Uri("https://test.service.url/"))); } [Fact] @@ -66,7 +66,7 @@ public async Task SendActivityAsync_WithNullConversation_ThrowsArgumentNullExcep }; await Assert.ThrowsAsync(() => - conversationClient.SendActivityAsync(activity)); + conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/"))); } [Fact] @@ -83,11 +83,11 @@ public async Task SendActivityAsync_WithEmptyConversationId_ThrowsArgumentExcept }; await Assert.ThrowsAsync(() => - conversationClient.SendActivityAsync(activity)); + conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/"))); } [Fact] - public async Task SendActivityAsync_WithNullServiceUrl_ThrowsArgumentNullException() + public async Task SendActivityAsync_WithNullServiceUrlParameter_ThrowsArgumentNullException() { HttpClient httpClient = new(); ConversationClient conversationClient = new(httpClient); @@ -99,7 +99,7 @@ public async Task SendActivityAsync_WithNullServiceUrl_ThrowsArgumentNullExcepti }; await Assert.ThrowsAsync(() => - conversationClient.SendActivityAsync(activity)); + conversationClient.SendActivityAsync(activity, null!)); } [Fact] @@ -129,7 +129,7 @@ public async Task SendActivityAsync_WithHttpError_ThrowsHttpRequestException() }; HttpRequestException exception = await Assert.ThrowsAsync(() => - conversationClient.SendActivityAsync(activity)); + conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/"))); Assert.Contains("Error sending activity", exception.Message); Assert.Contains("BadRequest", exception.Message); @@ -159,11 +159,11 @@ public async Task SendActivityAsync_ConstructsCorrectUrl() CoreActivity activity = new() { Type = ActivityType.Message, - ServiceUrl = new Uri("https://test.service.url/"), + ServiceUrl = new Uri("https://ignored.service.url/"), Conversation = new("conv123") }; - await conversationClient.SendActivityAsync(activity); + await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(capturedRequest); Assert.Equal("https://test.service.url/v3/conversations/conv123/activities/", capturedRequest.RequestUri?.ToString()); @@ -199,7 +199,7 @@ public async Task SendActivityAsync_WithIsTargeted_AppendsQueryString() Recipient = new ChannelAccount { IsTargeted = true } }; - await conversationClient.SendActivityAsync(activity); + await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(capturedRequest); Assert.Contains("isTargetedActivity=true", capturedRequest.RequestUri?.ToString()); @@ -232,7 +232,7 @@ public async Task UpdateActivityAsync_WithIsTargeted_AppendsQueryString() ServiceUrl = new Uri("https://test.service.url/") }; - await conversationClient.UpdateActivityAsync("conv123", "activity123", activity, isTargeted: true); + await conversationClient.UpdateActivityAsync("conv123", "activity123", activity, new Uri("https://test.service.url/"), isTargeted: true); Assert.NotNull(capturedRequest); Assert.Contains("isTargetedActivity=true", capturedRequest.RequestUri?.ToString()); @@ -297,7 +297,7 @@ public async Task DeleteActivityAsync_WithActivity_UsesIsTargetedProperty() ServiceUrl = new Uri("https://test.service.url/") }; - await conversationClient.DeleteActivityAsync("conv123", activity, isTargeted: true); + await conversationClient.DeleteActivityAsync("conv123", activity, new Uri("https://test.service.url/"), isTargeted: true); Assert.NotNull(capturedRequest); Assert.Contains("isTargetedActivity=true", capturedRequest.RequestUri?.ToString()); @@ -336,7 +336,7 @@ public async Task UpdateTargetedActivityAsync_AppendsQueryStringWithoutRecipient ServiceUrl = new Uri("https://test.service.url/"), }; - await conversationClient.UpdateTargetedActivityAsync("conv123", "activity123", activity); + await conversationClient.UpdateTargetedActivityAsync("conv123", "activity123", activity, new Uri("https://test.service.url/")); Assert.NotNull(capturedRequest); Assert.Contains("isTargetedActivity=true", capturedRequest.RequestUri?.ToString()); @@ -405,7 +405,7 @@ public async Task SendActivityAsync_WithAgentsChannel_TruncatesConversationId() Conversation = new(longConversationId) }; - await conversationClient.SendActivityAsync(activity); + await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(capturedRequest); string expectedTruncatedId = "acf"; @@ -444,7 +444,7 @@ public async Task SendActivityAsync_WithRecipientIsTargeted_DeserializedFromJson """; CoreActivity activity = CoreActivity.FromJsonString(activityJson); - await conversationClient.SendActivityAsync(activity); + await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(capturedRequest); Assert.Contains("isTargetedActivity=true", capturedRequest.RequestUri?.ToString()); @@ -482,7 +482,7 @@ public async Task SendActivityAsync_WithJsonElementFrom_ExtractsAgenticIdentity( """; CoreActivity activity = CoreActivity.FromJsonString(activityJson); - await conversationClient.SendActivityAsync(activity); + await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); // Verify the request was made (agenticIdentity is passed to BotHttpClient via request options) Assert.NotNull(capturedRequest); @@ -518,7 +518,7 @@ public async Task SendActivityAsync_WithChannelAccountFrom_ExtractsAgenticIdenti From = from }; - SendActivityResponse? result = await conversationClient.SendActivityAsync(activity); + SendActivityResponse? result = await conversationClient.SendActivityAsync(activity, new Uri("https://test.service.url/")); Assert.NotNull(result); } diff --git a/core/test/Microsoft.Teams.Core.UnitTests/CoreActivityBuilderTests.cs b/core/test/Microsoft.Teams.Core.UnitTests/CoreActivityBuilderTests.cs index fa8afa59..cbdbce5e 100644 --- a/core/test/Microsoft.Teams.Core.UnitTests/CoreActivityBuilderTests.cs +++ b/core/test/Microsoft.Teams.Core.UnitTests/CoreActivityBuilderTests.cs @@ -46,18 +46,6 @@ public void WithId_SetsActivityId() Assert.Equal("test-activity-id", activity.Id); } - [Fact] - public void WithServiceUrl_SetsServiceUrl() - { - Uri serviceUrl = new("https://smba.trafficmanager.net/teams/"); - - CoreActivity activity = new CoreActivityBuilder() - .WithServiceUrl(serviceUrl) - .Build(); - - Assert.Equal(serviceUrl, activity.ServiceUrl); - } - [Fact] public void WithChannelId_SetsChannelId() { @@ -96,7 +84,6 @@ public void FluentAPI_CompleteActivity_BuildsCorrectly() .WithId("activity-123") .WithChannelId("msteams") .WithProperty("text", "Test message") - .WithServiceUrl(new Uri("https://smba.trafficmanager.net/teams/")) .Build(); Assert.Equal(ActivityType.Message, activity.Type); @@ -196,14 +183,4 @@ public void Build_AfterModificationThenBuild_ReflectsChanges() Assert.Equal("id-2", activity2.Id); } - [Fact] - public void WithServiceUrl_String_SetsServiceUrl() - { - CoreActivity activity = new CoreActivityBuilder() - .WithServiceUrl("https://smba.trafficmanager.net/teams/") - .Build(); - - Assert.Equal(new Uri("https://smba.trafficmanager.net/teams/"), activity.ServiceUrl); - } - } diff --git a/core/test/Microsoft.Teams.Core.UnitTests/Diagnostics/TelemetryTests.cs b/core/test/Microsoft.Teams.Core.UnitTests/Diagnostics/TelemetryTests.cs index aa9010fb..cd53faa8 100644 --- a/core/test/Microsoft.Teams.Core.UnitTests/Diagnostics/TelemetryTests.cs +++ b/core/test/Microsoft.Teams.Core.UnitTests/Diagnostics/TelemetryTests.cs @@ -114,7 +114,7 @@ public async Task ConversationClient_SendActivityAsync_EmitsConversationClientSp Type = ActivityType.Message, ServiceUrl = new Uri("https://smba.example/"), Conversation = new("conv-1"), - }); + }, new Uri("https://smba.example/")); Assert.NotNull(response); Activity span = Assert.Single(spanCapture.Stopped, a => a.OperationName == "conversation_client"); @@ -144,7 +144,7 @@ await Assert.ThrowsAsync(() => client.SendActivityAsync(ne Type = ActivityType.Message, ServiceUrl = new Uri("https://smba.example/"), Conversation = new("conv-1"), - })); + }, new Uri("https://smba.example/"))); Activity span = Assert.Single(spanCapture.Stopped, a => a.OperationName == "conversation_client"); Assert.Equal(ActivityStatusCode.Error, span.Status);