From a3faf3ce46349d119f80034d56b9cc4e4b9bb97a Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Fri, 22 May 2026 12:06:29 -0700 Subject: [PATCH 1/7] dependencies record --- core/samples/CustomHosting/MyTeamsBotApp.cs | 5 +- ...{TeamsBotAppHandlers.cs => ExtAIBotApp.cs} | 40 +++++---- core/samples/ExtAIBot/Program.cs | 16 ++-- core/samples/ExtAIBot/appsettings.json | 2 +- .../TeamsBotApplication.HostingExtensions.cs | 1 + .../TeamsBotApplication.cs | 56 +++++++------ .../TeamsBotApplicationDependencies.cs | 32 ++++++++ .../OAuthFlowTests.cs | 4 +- .../PromptPreviewTests.cs | 4 +- ...amsBotApplicationHostingExtensionsTests.cs | 81 +++++++++++++++++++ .../TeamsBotApplicationTests.cs | 4 +- 11 files changed, 184 insertions(+), 61 deletions(-) rename core/samples/ExtAIBot/{TeamsBotAppHandlers.cs => ExtAIBotApp.cs} (73%) create mode 100644 core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs create mode 100644 core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs diff --git a/core/samples/CustomHosting/MyTeamsBotApp.cs b/core/samples/CustomHosting/MyTeamsBotApp.cs index f2498d401..d2a28299c 100644 --- a/core/samples/CustomHosting/MyTeamsBotApp.cs +++ b/core/samples/CustomHosting/MyTeamsBotApp.cs @@ -2,16 +2,13 @@ // Licensed under the MIT License. using Microsoft.Teams.Apps; -using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Apps.Handlers; -using Microsoft.Teams.Core; -using Microsoft.Teams.Core.Hosting; namespace CustomHosting; public class MyTeamsBotApp : TeamsBotApplication { - public MyTeamsBotApp(ConversationClient conversationClient, UserTokenClient userTokenClient, ApiClient teamsApiClient, IHttpContextAccessor httpContextAccessor, ILogger logger, BotApplicationOptions? options = null) : base(conversationClient, userTokenClient, teamsApiClient, httpContextAccessor, logger, options) + public MyTeamsBotApp(TeamsBotApplicationDependencies deps) : base(deps) { this.OnMessage(async (ctx, ct) => { diff --git a/core/samples/ExtAIBot/TeamsBotAppHandlers.cs b/core/samples/ExtAIBot/ExtAIBotApp.cs similarity index 73% rename from core/samples/ExtAIBot/TeamsBotAppHandlers.cs rename to core/samples/ExtAIBot/ExtAIBotApp.cs index 3d95d6cf4..72cc6d283 100644 --- a/core/samples/ExtAIBot/TeamsBotAppHandlers.cs +++ b/core/samples/ExtAIBot/ExtAIBotApp.cs @@ -10,35 +10,45 @@ namespace ExtAIBot; -// Bot activity handlers: incoming messages, clarification-card submits, and the -// custom-feedback fetch/submit pair. Each handler ultimately funnels a user-supplied -// string back through the Agent. -internal static class TeamsBotAppHandlers +// Teams bot subclass: wires the four activity handlers (message, clarification card +// submit, custom-feedback fetch/submit) in its constructor. Each handler funnels a +// user-supplied string back through the Agent. +internal class ExtAIBotApp : TeamsBotApplication { - public static TeamsBotApplication RegisterHandlers(this TeamsBotApplication teamsApp, Agent agent, ILogger logger) + private readonly Agent _agent; + private readonly ILogger _logger; + + public ExtAIBotApp( + TeamsBotApplicationDependencies dependencies, + Agent agent, + ILogger logger) + : base(dependencies) { + _agent = agent; + _logger = logger; + // Message handler. - teamsApp.OnMessage(async (context, cancellationToken) => + this.OnMessage(async (context, cancellationToken) => { string userText = context.Activity.TextWithoutMentions ?? ""; - await RespondAsync(agent, context, userText, cancellationToken); + await RespondAsync(context, userText, cancellationToken); }); // Clarification: adaptive card action. // Triggered when the user submits the clarification card (Action.Execute, verb "clarification"). - teamsApp.OnAdaptiveCardAction(async (context, cancellationToken) => + this.OnAdaptiveCardAction(async (context, cancellationToken) => { if (context.Activity.Value?.Action?.Verb == "clarification") { string choice = context.Activity.Value.Action.Data?["clarificationChoice"]?.ToString() ?? ""; - await RespondAsync(agent, context, choice, cancellationToken); + await RespondAsync(context, choice, cancellationToken); } return InvokeResponse.Ok(); }); // Feedback: message fetch task. // Triggered when the user clicks thumbs up or thumbs down on a bot reply. - teamsApp.OnMessageFetchTask((context, cancellationToken) => + this.OnMessageFetchTask((context, cancellationToken) => { string? reaction = context.Activity.Value?.Data?.ActionValue?.Reaction; @@ -52,28 +62,26 @@ public static TeamsBotApplication RegisterHandlers(this TeamsBotApplication team }); // Feedback: message submit action. - teamsApp.OnMessageSubmitFeedback((context, cancellationToken) => + this.OnMessageSubmitFeedback((context, cancellationToken) => { MessageSubmitFeedbackValue? feedback = context.Activity.Value; - logger.LogInformation("Feedback received — reaction: {Reaction}, feedback: {Feedback}", + _logger.LogInformation("Feedback received — reaction: {Reaction}, feedback: {Feedback}", feedback?.Reaction, feedback?.Feedback); return Task.FromResult(InvokeResponse.Ok()); }); - - return teamsApp; } // Runs the agent and streams a response back. Shared between the incoming-message // handler and the clarification-card submit handler — both flows ultimately just // feed a user-supplied string into the agent. - private static async Task RespondAsync(Agent agent, Context context, string userText, CancellationToken cancellationToken) + private async Task RespondAsync(Context context, string userText, CancellationToken cancellationToken) where TActivity : TeamsActivity { _ = context.Activity.Conversation?.Id ?? throw new InvalidOperationException("Missing conversation ID."); TeamsStreamingWriter writer = TeamsStreamingWriter.CreateFromContext(context); - RunResult result = await agent.RunAsync(context.Activity.Conversation!.Id, userText, writer, cancellationToken); + RunResult result = await _agent.RunAsync(context.Activity.Conversation!.Id, userText, writer, cancellationToken); IList entities = result.Citations.BuildEntities(result.FullText); diff --git a/core/samples/ExtAIBot/Program.cs b/core/samples/ExtAIBot/Program.cs index 4101d4ede..c772b24d0 100644 --- a/core/samples/ExtAIBot/Program.cs +++ b/core/samples/ExtAIBot/Program.cs @@ -7,21 +7,20 @@ using Microsoft.Extensions.AI; using Microsoft.Teams.Apps; -// Wires up the Teams bot application and delegates AI execution to Agent. -// Handler registration lives in TeamsBotAppHandlers.cs. +// Wires up the Teams bot application. Handler registration lives in ExtAIBotApp. WebApplicationBuilder builder = WebApplication.CreateSlimBuilder(args); -builder.Services.AddTeamsBotApplication(); +builder.Services.AddTeamsBotApplication(); builder.Services.AddSingleton(sp => { IConfiguration config = sp.GetRequiredService(); string endpoint = config["AzureOpenAI:Endpoint"] ?? throw new InvalidOperationException("AzureOpenAI:Endpoint is required."); string apiKey = config["AzureOpenAI:ApiKey"] ?? throw new InvalidOperationException("AzureOpenAI:ApiKey is required."); - string modelId = config["AzureOpenAI:ModelId"] ?? throw new InvalidOperationException("AzureOpenAI:ModelId is required."); + string deployment = config["AzureOpenAI:Deployment"] ?? throw new InvalidOperationException("AzureOpenAI:Deployment is required."); return new AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey)) - .GetChatClient(modelId) + .GetChatClient(deployment) .AsIChatClient() .AsBuilder() .UseFunctionInvocation() @@ -34,10 +33,5 @@ builder.Services.AddSingleton(); WebApplication webApp = builder.Build(); - -Agent agent = webApp.Services.GetRequiredService(); -ILogger handlerLogger = webApp.Services.GetRequiredService().CreateLogger("ExtAIBot.TeamsBotAppHandlers"); - -webApp.UseTeamsBotApplication().RegisterHandlers(agent, handlerLogger); - +webApp.UseTeamsBotApplication(); webApp.Run(); diff --git a/core/samples/ExtAIBot/appsettings.json b/core/samples/ExtAIBot/appsettings.json index 057db1509..6c9fd3f11 100644 --- a/core/samples/ExtAIBot/appsettings.json +++ b/core/samples/ExtAIBot/appsettings.json @@ -10,6 +10,6 @@ "AzureOpenAI": { "Endpoint": "", "ApiKey": "", - "ModelId": "" + "Deployment": "" } } diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs index cbb2e9f09..5050de575 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs @@ -113,6 +113,7 @@ public static IServiceCollection AddTeamsBotApplication(this IServiceColle services.AddBotApplication(botConfig); services.AddBotClient(nameof(ApiClient), botConfig); + services.AddSingleton(); return services; } diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs index 3536f5eac..326d0c1b5 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs @@ -81,36 +81,46 @@ public OAuthFlow GetOAuthFlow(string connectionName) /// public ApiClient Api { get; } - /// The conversation client for sending and managing activities. - /// The user token client for OAuth operations. - /// The Teams API client for Teams-specific operations. - /// The HTTP context accessor for reading invoke responses. - /// The logger instance. - /// Options containing the application (client) ID, used for logging and diagnostics. Defaults to an empty instance if not provided. - /// Teams-specific options including OAuth flow configuration. Defaults to an empty instance if not provided. - public TeamsBotApplication( - ConversationClient conversationClient, - UserTokenClient userTokenClient, - ApiClient teamsApiClient, - IHttpContextAccessor httpContextAccessor, - ILogger logger, - BotApplicationOptions? options = null, - TeamsBotApplicationOptions? teamsOptions = null) - : base(conversationClient, userTokenClient, logger, options) + /// + /// Initializes a new instance using a bundled . + /// Designed for subclassing: derived types declare a single-parameter constructor that + /// forwards to this overload. + /// + /// The bundled dependencies. Cannot be null. + /// + /// + /// public class MyBot : TeamsBotApplication + /// { + /// public MyBot(TeamsBotApplicationDependencies deps) : base(deps) + /// { + /// this.OnMessage(async (ctx, ct) => + /// await ctx.SendActivityAsync("Hello!", ct)); + /// } + /// } + /// + /// + public TeamsBotApplication(TeamsBotApplicationDependencies dependencies) + : base( + (dependencies ?? throw new ArgumentNullException(nameof(dependencies))).ConversationClient, + dependencies.UserTokenClient, + dependencies.Logger, + dependencies.Options) { - _teamsApiClient = teamsApiClient; - Api = teamsApiClient; - Logger = logger; - Router = new Router(logger); + _teamsApiClient = dependencies.TeamsApiClient; + Api = dependencies.TeamsApiClient; + Logger = dependencies.Logger; + Router = new Router(dependencies.Logger); - // Auto-register OAuth flows from DI options - if (teamsOptions is not null) + if (dependencies.TeamsOptions is not null) { - foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in teamsOptions.OAuthFlows) + foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in dependencies.TeamsOptions.OAuthFlows) { this.AddOAuthFlow(descriptor.Options); } } + + IHttpContextAccessor httpContextAccessor = dependencies.HttpContextAccessor; + ILogger logger = dependencies.Logger; OnActivity = async (activity, cancellationToken) => { logger.LogDebug("OnActivity invoked for activity: Id={Id}", activity.Id); diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs new file mode 100644 index 000000000..a2e6a6c02 --- /dev/null +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Teams.Apps.Api.Clients; +using Microsoft.Teams.Core; +using Microsoft.Teams.Core.Hosting; + +namespace Microsoft.Teams.Apps; + +/// +/// Bundles the dependencies required to construct a . +/// Pass a single TeamsBotApplicationDependencies to the base constructor when subclassing, +/// so derived types do not need to thread every dependency by hand. +/// +/// +/// +/// public class MyBot : TeamsBotApplication +/// { +/// public MyBot(TeamsBotApplicationDependencies deps) : base(deps) { } +/// } +/// +/// +public sealed record TeamsBotApplicationDependencies( + ConversationClient ConversationClient, + UserTokenClient UserTokenClient, + ApiClient TeamsApiClient, + IHttpContextAccessor HttpContextAccessor, + ILogger Logger, + BotApplicationOptions? Options = null, + TeamsBotApplicationOptions? TeamsOptions = null); diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs index 5e0cf2a31..bae8e1800 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs @@ -426,13 +426,13 @@ private static TestHarness CreateHarness(params string[] connectionNames) mockConversationClient.Object, mockUserTokenClient.Object); - TeamsBotApplication app = new( + TeamsBotApplication app = new(new TeamsBotApplicationDependencies( mockConversationClient.Object, mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new BotApplicationOptions { AppId = "test-app-id" })); OAuthFlow? graphFlow = null; OAuthFlow? githubFlow = null; diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs index 27da03854..49d07552f 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs @@ -219,13 +219,13 @@ private static TestHarness CreateHarness() mockConversationClient.Object, mockUserTokenClient.Object); - TeamsBotApplication app = new( + TeamsBotApplication app = new(new TeamsBotApplicationDependencies( mockConversationClient.Object, mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new BotApplicationOptions { AppId = "test-app-id" })); return new TestHarness { diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs new file mode 100644 index 000000000..de0e8a008 --- /dev/null +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Teams.Apps.UnitTests; + +public class TeamsBotApplicationHostingExtensionsTests +{ + private static ServiceProvider BuildServiceProvider(Dictionary configData) + { + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configData) + .Build(); + + ServiceCollection services = new(); + services.AddSingleton(configuration); + services.AddLogging(); + services.AddTeamsBotApplication(); + + return services.BuildServiceProvider(); + } + + [Fact] + public void AddTeamsBotApplication_RegistersTeamsBotApplicationDependencies_WithAllFieldsPopulated() + { + Dictionary configData = new() + { + ["AzureAd:ClientId"] = "teams-bundle-client-id", + ["AzureAd:TenantId"] = "teams-bundle-tenant-id" + }; + + ServiceProvider serviceProvider = BuildServiceProvider(configData); + TeamsBotApplicationDependencies deps = serviceProvider.GetRequiredService(); + + Assert.NotNull(deps.ConversationClient); + Assert.NotNull(deps.UserTokenClient); + Assert.NotNull(deps.TeamsApiClient); + Assert.NotNull(deps.HttpContextAccessor); + Assert.NotNull(deps.Logger); + Assert.NotNull(deps.Options); + Assert.Equal("teams-bundle-client-id", deps.Options!.AppId); + Assert.NotNull(deps.TeamsOptions); + } + + [Fact] + public void AddTeamsBotApplication_WithCustomSubclass_ResolvesViaBundledCtor() + { + Dictionary configData = new() + { + ["AzureAd:ClientId"] = "subclass-client-id", + ["AzureAd:TenantId"] = "subclass-tenant-id" + }; + + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configData) + .Build(); + + ServiceCollection services = new(); + services.AddSingleton(configuration); + services.AddLogging(); + services.AddTeamsBotApplication(); + + using ServiceProvider serviceProvider = services.BuildServiceProvider(); + BundleSubclassBot bot = serviceProvider.GetRequiredService(); + + Assert.True(bot.ConstructedViaBundle); + Assert.Equal("subclass-client-id", bot.AppId); + } + + private sealed class BundleSubclassBot : TeamsBotApplication + { + public bool ConstructedViaBundle { get; } + + public BundleSubclassBot(TeamsBotApplicationDependencies services) : base(services) + { + ConstructedViaBundle = true; + } + } +} diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs index 968a9ec6e..b1cf38cd6 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs @@ -56,12 +56,12 @@ private static TeamsBotApplication CreateApp() mockConversationClient.Object, mockUserTokenClient.Object); - return new TeamsBotApplication( + return new TeamsBotApplication(new TeamsBotApplicationDependencies( mockConversationClient.Object, mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new BotApplicationOptions { AppId = "test-app-id" })); } } From 087bc7916809676bf943954584520a173320d579 Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Fri, 22 May 2026 12:28:27 -0700 Subject: [PATCH 2/7] test fix --- .../CompatBotAdapterTests.cs | 51 ++----------------- ...oft.Teams.Apps.BotBuilder.UnitTests.csproj | 1 - 2 files changed, 5 insertions(+), 47 deletions(-) diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs index e06cb9980..78724df20 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs @@ -4,9 +4,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Teams.Apps; -using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Core; using Microsoft.Teams.Core.Schema; using Moq; @@ -253,59 +252,19 @@ private static Mock CreateMockConversationClient() return mock; } - private static Mock CreateMockTeamsBotApplication() - { - Mock mockConversationClient = CreateMockConversationClient(); - Mock mockUserTokenClient = new( - new HttpClient(), - Mock.Of(), - NullLogger.Instance); - ApiClient mockTeamsApiClient = new( - new Uri("https://service.url"), - new HttpClient(), - mockConversationClient.Object, - mockUserTokenClient.Object, - NullLogger.Instance); - - Mock mock = new( - mockConversationClient.Object, - mockUserTokenClient.Object, - mockTeamsApiClient, - Mock.Of(), - NullLogger.Instance); - - return mock; - } - private static TeamsBotAdapter CreateCompatBotAdapter(ConversationClient conversationClient) { Mock mockUserTokenClient = new( new HttpClient(), - Mock.Of(), + Mock.Of(), NullLogger.Instance); - ApiClient mockTeamsApiClient = new( - new Uri("https://service.url"), - new HttpClient(), + BotApplication botApplication = new( conversationClient, mockUserTokenClient.Object, - NullLogger.Instance); - TeamsBotApplication teamsBotApplication = new( - conversationClient, - mockUserTokenClient.Object, - mockTeamsApiClient, - Mock.Of(), - NullLogger.Instance); + NullLogger.Instance); return new TeamsBotAdapter( - teamsBotApplication, - Mock.Of(), - NullLogger.Instance); - } - - private static TeamsBotAdapter CreateCompatBotAdapter(TeamsBotApplication teamsBotApplication) - { - return new TeamsBotAdapter( - teamsBotApplication, + botApplication, Mock.Of(), NullLogger.Instance); } diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj index 41c9b2702..961f74e2d 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj @@ -22,7 +22,6 @@ - From 74cfae8381b10c469ba9c0179d73b8e94fcd26b0 Mon Sep 17 00:00:00 2001 From: Mehak Bindra Date: Fri, 22 May 2026 12:29:44 -0700 Subject: [PATCH 3/7] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../TeamsBotApplicationHostingExtensionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs index de0e8a008..e3035ef5b 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -31,7 +31,7 @@ public void AddTeamsBotApplication_RegistersTeamsBotApplicationDependencies_With ["AzureAd:TenantId"] = "teams-bundle-tenant-id" }; - ServiceProvider serviceProvider = BuildServiceProvider(configData); + using ServiceProvider serviceProvider = BuildServiceProvider(configData); TeamsBotApplicationDependencies deps = serviceProvider.GetRequiredService(); Assert.NotNull(deps.ConversationClient); From 1c906d234e47d905fd384a3917033d863ff98b2f Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Fri, 22 May 2026 13:57:00 -0700 Subject: [PATCH 4/7] remove record --- core/samples/CustomHosting/MyTeamsBotApp.cs | 9 ++++- core/samples/ExtAIBot/ExtAIBotApp.cs | 10 +++-- .../Api/Clients/ApiClient.cs | 12 ++++++ .../TeamsBotApplication.HostingExtensions.cs | 3 +- .../TeamsBotApplication.cs | 40 ++++++++++--------- .../TeamsBotApplicationDependencies.cs | 32 --------------- .../TeamsBotApplicationOptions.cs | 4 +- .../Hosting/BotApplicationOptions.cs | 2 +- .../OAuthFlowTests.cs | 6 +-- .../PromptPreviewTests.cs | 6 +-- ...amsBotApplicationHostingExtensionsTests.cs | 39 +++++++++--------- .../TeamsBotApplicationTests.cs | 6 +-- 12 files changed, 81 insertions(+), 88 deletions(-) delete mode 100644 core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs diff --git a/core/samples/CustomHosting/MyTeamsBotApp.cs b/core/samples/CustomHosting/MyTeamsBotApp.cs index d2a28299c..26069248e 100644 --- a/core/samples/CustomHosting/MyTeamsBotApp.cs +++ b/core/samples/CustomHosting/MyTeamsBotApp.cs @@ -1,14 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.AspNetCore.Http; using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Apps.Handlers; namespace CustomHosting; public class MyTeamsBotApp : TeamsBotApplication { - public MyTeamsBotApp(TeamsBotApplicationDependencies deps) : base(deps) + public MyTeamsBotApp( + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) { this.OnMessage(async (ctx, ct) => { diff --git a/core/samples/ExtAIBot/ExtAIBotApp.cs b/core/samples/ExtAIBot/ExtAIBotApp.cs index 72cc6d283..eb5d54a13 100644 --- a/core/samples/ExtAIBot/ExtAIBotApp.cs +++ b/core/samples/ExtAIBot/ExtAIBotApp.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.AspNetCore.Http; using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Apps.Handlers; using Microsoft.Teams.Apps.Handlers.TaskModules; using Microsoft.Teams.Apps.Schema; @@ -19,10 +21,12 @@ internal class ExtAIBotApp : TeamsBotApplication private readonly ILogger _logger; public ExtAIBotApp( - TeamsBotApplicationDependencies dependencies, + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, Agent agent, - ILogger logger) - : base(dependencies) + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) { _agent = agent; _logger = logger; diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs index 3dc4556d0..233ec195c 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs @@ -30,6 +30,18 @@ public class ApiClient private readonly CoreConversationClient _conversationClient; private readonly CoreUserTokenClient _userTokenClient; + /// + /// The underlying Core conversation client. Exposed primarily so derived bot applications + /// can forward it to without taking a second dependency. + /// + public CoreConversationClient ConversationClient => _conversationClient; + + /// + /// The underlying Core user token client. Exposed primarily so derived bot applications + /// can forward it to without taking a second dependency. + /// + public CoreUserTokenClient UserTokenClient => _userTokenClient; + /// /// The service URL used by this client. /// Null when constructed without a service URL (DI-friendly constructor). diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs index 5050de575..49c87046f 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs @@ -107,13 +107,12 @@ public static IServiceCollection AddTeamsBotApplication(this IServiceColle BotConfig botConfig = BotConfig.Resolve(services, sectionName); // Register TeamsBotApplicationOptions - TeamsBotApplicationOptions teamsOptions = new(); + TeamsBotApplicationOptions teamsOptions = new() { AppId = botConfig.ClientId }; configure?.Invoke(teamsOptions); services.AddSingleton(teamsOptions); services.AddBotApplication(botConfig); services.AddBotClient(nameof(ApiClient), botConfig); - services.AddSingleton(); return services; } diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs index 326d0c1b5..a4ff7ebf3 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs @@ -82,16 +82,18 @@ public OAuthFlow GetOAuthFlow(string connectionName) public ApiClient Api { get; } /// - /// Initializes a new instance using a bundled . - /// Designed for subclassing: derived types declare a single-parameter constructor that - /// forwards to this overload. + /// Initializes a new . /// - /// The bundled dependencies. Cannot be null. + /// The Teams API facade. Also carries the underlying Core conversation and user-token clients. + /// Accessor used to write invoke responses back to the current HTTP request. + /// Logger used by the bot and exposed as . + /// Optional Teams bot options (AppId, OAuth flows, etc.). /// /// /// public class MyBot : TeamsBotApplication /// { - /// public MyBot(TeamsBotApplicationDependencies deps) : base(deps) + /// public MyBot(ApiClient api, IHttpContextAccessor accessor, ILogger<MyBot> logger, TeamsBotApplicationOptions? options = null) + /// : base(api, accessor, logger, options) /// { /// this.OnMessage(async (ctx, ct) => /// await ctx.SendActivityAsync("Hello!", ct)); @@ -99,28 +101,30 @@ public OAuthFlow GetOAuthFlow(string connectionName) /// } /// /// - public TeamsBotApplication(TeamsBotApplicationDependencies dependencies) + public TeamsBotApplication( + ApiClient teamsApiClient, + IHttpContextAccessor httpContextAccessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) : base( - (dependencies ?? throw new ArgumentNullException(nameof(dependencies))).ConversationClient, - dependencies.UserTokenClient, - dependencies.Logger, - dependencies.Options) + (teamsApiClient ?? throw new ArgumentNullException(nameof(teamsApiClient))).ConversationClient, + teamsApiClient.UserTokenClient, + logger, + options) { - _teamsApiClient = dependencies.TeamsApiClient; - Api = dependencies.TeamsApiClient; - Logger = dependencies.Logger; - Router = new Router(dependencies.Logger); + _teamsApiClient = teamsApiClient; + Api = teamsApiClient; + Logger = logger; + Router = new Router(logger); - if (dependencies.TeamsOptions is not null) + if (options is not null) { - foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in dependencies.TeamsOptions.OAuthFlows) + foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in options.OAuthFlows) { this.AddOAuthFlow(descriptor.Options); } } - IHttpContextAccessor httpContextAccessor = dependencies.HttpContextAccessor; - ILogger logger = dependencies.Logger; OnActivity = async (activity, cancellationToken) => { logger.LogDebug("OnActivity invoked for activity: Id={Id}", activity.Id); diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs deleted file mode 100644 index a2e6a6c02..000000000 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationDependencies.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Teams.Apps.Api.Clients; -using Microsoft.Teams.Core; -using Microsoft.Teams.Core.Hosting; - -namespace Microsoft.Teams.Apps; - -/// -/// Bundles the dependencies required to construct a . -/// Pass a single TeamsBotApplicationDependencies to the base constructor when subclassing, -/// so derived types do not need to thread every dependency by hand. -/// -/// -/// -/// public class MyBot : TeamsBotApplication -/// { -/// public MyBot(TeamsBotApplicationDependencies deps) : base(deps) { } -/// } -/// -/// -public sealed record TeamsBotApplicationDependencies( - ConversationClient ConversationClient, - UserTokenClient UserTokenClient, - ApiClient TeamsApiClient, - IHttpContextAccessor HttpContextAccessor, - ILogger Logger, - BotApplicationOptions? Options = null, - TeamsBotApplicationOptions? TeamsOptions = null); diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs index e26146070..deccf72f2 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs @@ -2,13 +2,15 @@ // Licensed under the MIT License. using Microsoft.Teams.Apps.OAuth; +using Microsoft.Teams.Core.Hosting; namespace Microsoft.Teams.Apps; /// /// Options for configuring a . +/// Inherits so a single options object covers both Core and Teams settings. /// -public sealed class TeamsBotApplicationOptions +public sealed class TeamsBotApplicationOptions : BotApplicationOptions { internal List OAuthFlows { get; } = []; diff --git a/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs b/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs index bc2221bb5..0b14d46df 100644 --- a/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs +++ b/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs @@ -6,7 +6,7 @@ namespace Microsoft.Teams.Core.Hosting; /// /// Options for configuring a bot application instance. /// -public sealed class BotApplicationOptions +public class BotApplicationOptions { /// /// Gets or sets the application (client) ID, used for logging and diagnostics. diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs index bae8e1800..c3465896d 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs @@ -426,13 +426,11 @@ private static TestHarness CreateHarness(params string[] connectionNames) mockConversationClient.Object, mockUserTokenClient.Object); - TeamsBotApplication app = new(new TeamsBotApplicationDependencies( - mockConversationClient.Object, - mockUserTokenClient.Object, + TeamsBotApplication app = new( apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" })); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); OAuthFlow? graphFlow = null; OAuthFlow? githubFlow = null; diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs index 49d07552f..9612f2e9b 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs @@ -219,13 +219,11 @@ private static TestHarness CreateHarness() mockConversationClient.Object, mockUserTokenClient.Object); - TeamsBotApplication app = new(new TeamsBotApplicationDependencies( - mockConversationClient.Object, - mockUserTokenClient.Object, + TeamsBotApplication app = new( apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" })); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); return new TestHarness { diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs index e3035ef5b..b1dd1d446 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Teams.Apps.Api.Clients; namespace Microsoft.Teams.Apps.UnitTests; @@ -23,7 +26,7 @@ private static ServiceProvider BuildServiceProvider(Dictionary } [Fact] - public void AddTeamsBotApplication_RegistersTeamsBotApplicationDependencies_WithAllFieldsPopulated() + public void AddTeamsBotApplication_RegistersAllRequiredServices() { Dictionary configData = new() { @@ -32,20 +35,15 @@ public void AddTeamsBotApplication_RegistersTeamsBotApplicationDependencies_With }; using ServiceProvider serviceProvider = BuildServiceProvider(configData); - TeamsBotApplicationDependencies deps = serviceProvider.GetRequiredService(); - Assert.NotNull(deps.ConversationClient); - Assert.NotNull(deps.UserTokenClient); - Assert.NotNull(deps.TeamsApiClient); - Assert.NotNull(deps.HttpContextAccessor); - Assert.NotNull(deps.Logger); - Assert.NotNull(deps.Options); - Assert.Equal("teams-bundle-client-id", deps.Options!.AppId); - Assert.NotNull(deps.TeamsOptions); + Assert.NotNull(serviceProvider.GetRequiredService()); + Assert.NotNull(serviceProvider.GetRequiredService()); + TeamsBotApplicationOptions options = serviceProvider.GetRequiredService(); + Assert.Equal("teams-bundle-client-id", options.AppId); } [Fact] - public void AddTeamsBotApplication_WithCustomSubclass_ResolvesViaBundledCtor() + public void AddTeamsBotApplication_WithCustomSubclass_ResolvesViaDI() { Dictionary configData = new() { @@ -60,22 +58,27 @@ public void AddTeamsBotApplication_WithCustomSubclass_ResolvesViaBundledCtor() ServiceCollection services = new(); services.AddSingleton(configuration); services.AddLogging(); - services.AddTeamsBotApplication(); + services.AddTeamsBotApplication(); using ServiceProvider serviceProvider = services.BuildServiceProvider(); - BundleSubclassBot bot = serviceProvider.GetRequiredService(); + SubclassBot bot = serviceProvider.GetRequiredService(); - Assert.True(bot.ConstructedViaBundle); + Assert.True(bot.ConstructedViaDI); Assert.Equal("subclass-client-id", bot.AppId); } - private sealed class BundleSubclassBot : TeamsBotApplication + private sealed class SubclassBot : TeamsBotApplication { - public bool ConstructedViaBundle { get; } + public bool ConstructedViaDI { get; } - public BundleSubclassBot(TeamsBotApplicationDependencies services) : base(services) + public SubclassBot( + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) { - ConstructedViaBundle = true; + ConstructedViaDI = true; } } } diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs index b1cf38cd6..75c8f9b22 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs @@ -56,12 +56,10 @@ private static TeamsBotApplication CreateApp() mockConversationClient.Object, mockUserTokenClient.Object); - return new TeamsBotApplication(new TeamsBotApplicationDependencies( - mockConversationClient.Object, - mockUserTokenClient.Object, + return new TeamsBotApplication( apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" })); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); } } From 1b3c0f46e240ab467add903ff12e2ea8ae5a5056 Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Fri, 22 May 2026 14:05:29 -0700 Subject: [PATCH 5/7] fix --- .../Api/Clients/ApiClient.cs | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs index 233ec195c..1fb29248b 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs @@ -27,20 +27,10 @@ namespace Microsoft.Teams.Apps.Api.Clients; public class ApiClient { private readonly BotHttpClient _http; - private readonly CoreConversationClient _conversationClient; - private readonly CoreUserTokenClient _userTokenClient; - /// - /// The underlying Core conversation client. Exposed primarily so derived bot applications - /// can forward it to without taking a second dependency. - /// - public CoreConversationClient ConversationClient => _conversationClient; + internal CoreConversationClient ConversationClient { get; } - /// - /// The underlying Core user token client. Exposed primarily so derived bot applications - /// can forward it to without taking a second dependency. - /// - public CoreUserTokenClient UserTokenClient => _userTokenClient; + internal CoreUserTokenClient UserTokenClient { get; } /// /// The service URL used by this client. @@ -90,8 +80,8 @@ public ApiClient(HttpClient httpClient, CoreConversationClient conversationClien ArgumentNullException.ThrowIfNull(userTokenClient); _http = new BotHttpClient(httpClient, logger); - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; Bots = new BotClient(userTokenClient); Users = new UserClient(userTokenClient); @@ -118,8 +108,8 @@ public ApiClient(Uri serviceUrl, HttpClient httpClient, CoreConversationClient c ArgumentNullException.ThrowIfNull(userTokenClient); _http = new BotHttpClient(httpClient, logger); - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; ServiceUrl = serviceUrl; Bots = new BotClient(userTokenClient); Conversations = new ConversationApiClient(serviceUrl, conversationClient); @@ -137,8 +127,8 @@ public ApiClient(ApiClient client) ServiceUrl = client.ServiceUrl; _http = client._http; - _conversationClient = client._conversationClient; - _userTokenClient = client._userTokenClient; + ConversationClient = client.ConversationClient; + UserTokenClient = client.UserTokenClient; Bots = client.Bots; Conversations = client.Conversations; Users = client.Users; @@ -150,8 +140,8 @@ public ApiClient(ApiClient client) private ApiClient(BotHttpClient http, CoreConversationClient conversationClient, CoreUserTokenClient userTokenClient, Uri serviceUrl) { _http = http; - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; ServiceUrl = serviceUrl; Bots = new BotClient(userTokenClient); Conversations = new ConversationApiClient(serviceUrl, conversationClient); @@ -169,6 +159,6 @@ private ApiClient(BotHttpClient http, CoreConversationClient conversationClient, public virtual ApiClient ForServiceUrl(Uri serviceUrl) { ArgumentNullException.ThrowIfNull(serviceUrl); - return new ApiClient(_http, _conversationClient, _userTokenClient, serviceUrl); + return new ApiClient(_http, ConversationClient, UserTokenClient, serviceUrl); } } From f465990ecf49c8bcdd99bd1a48f51c844ae1a7d8 Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Wed, 27 May 2026 16:33:35 -0700 Subject: [PATCH 6/7] nits --- core/samples/ExtAIBot/ExtAIBotApp.cs | 2 +- .../TeamsBotApplicationHostingExtensionsTests.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/samples/ExtAIBot/ExtAIBotApp.cs b/core/samples/ExtAIBot/ExtAIBotApp.cs index eb5d54a13..2436c8de1 100644 --- a/core/samples/ExtAIBot/ExtAIBotApp.cs +++ b/core/samples/ExtAIBot/ExtAIBotApp.cs @@ -21,10 +21,10 @@ internal class ExtAIBotApp : TeamsBotApplication private readonly ILogger _logger; public ExtAIBotApp( + Agent agent, ApiClient api, IHttpContextAccessor accessor, ILogger logger, - Agent agent, TeamsBotApplicationOptions? options = null) : base(api, accessor, logger, options) { diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs index b1dd1d446..68be075e1 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -40,6 +40,10 @@ public void AddTeamsBotApplication_RegistersAllRequiredServices() Assert.NotNull(serviceProvider.GetRequiredService()); TeamsBotApplicationOptions options = serviceProvider.GetRequiredService(); Assert.Equal("teams-bundle-client-id", options.AppId); + + BotApplicationOptions botOptions = serviceProvider.GetRequiredService(); + Assert.Equal("teams-bundle-client-id", botOptions.AppId); + Assert.Equal(options.AppId, botOptions.AppId); } [Fact] From dfd10284595279e6c1457e82dca8b003c3d32c49 Mon Sep 17 00:00:00 2001 From: MehakBindra Date: Wed, 27 May 2026 16:37:41 -0700 Subject: [PATCH 7/7] fix --- .../TeamsBotApplicationHostingExtensionsTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs index 68be075e1..229aa4dc5 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Teams.Apps.Api.Clients; +using Microsoft.Teams.Core.Hosting; namespace Microsoft.Teams.Apps.UnitTests;