From de4a3069f8625c018f69f59491fd940714ec77e5 Mon Sep 17 00:00:00 2001 From: Shawn Jackson Date: Thu, 6 Aug 2026 07:15:06 -0700 Subject: [PATCH] RG-T117 Fixing autofac dep issue --- .../Services/ChatbotAdapterRegistry.cs | 18 ++++++++++----- .../Chatbot/ChatbotOutboundTests.cs | 23 +++++++++++++++++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Providers/Resgrid.Providers.Chatbot/Services/ChatbotAdapterRegistry.cs b/Providers/Resgrid.Providers.Chatbot/Services/ChatbotAdapterRegistry.cs index 56ecfa7e5..600495243 100644 --- a/Providers/Resgrid.Providers.Chatbot/Services/ChatbotAdapterRegistry.cs +++ b/Providers/Resgrid.Providers.Chatbot/Services/ChatbotAdapterRegistry.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Resgrid.Chatbot.Models; @@ -11,17 +12,22 @@ namespace Resgrid.Providers.Chatbot.Services /// public class ChatbotAdapterRegistry : IChatbotAdapterRegistry { - private readonly Dictionary _adapters; + // Adapters are resolved lazily: WebChatAdapter's notifier pulls in the chat services, which + // reach AuthorizationService -> CallsService -> CommunicationService -> ChatbotOutboundService + // and back into this registry. Deferring adapter activation until first GetAdapter call keeps + // that loop out of the container's constructor chain (Autofac circular dependency exception). + private readonly Lazy> _adapters; - public ChatbotAdapterRegistry(IEnumerable adapters) + public ChatbotAdapterRegistry(Lazy> adapters) { - _adapters = (adapters ?? Enumerable.Empty()) - .GroupBy(a => a.Platform) - .ToDictionary(g => g.Key, g => g.First()); + _adapters = new Lazy>(() => + (adapters?.Value ?? Enumerable.Empty()) + .GroupBy(a => a.Platform) + .ToDictionary(g => g.Key, g => g.First())); } public IChatbotPlatformAdapter GetAdapter(ChatbotPlatform platform) - => _adapters.TryGetValue(platform, out var adapter) ? adapter : null; + => _adapters.Value.TryGetValue(platform, out var adapter) ? adapter : null; /// /// Whether the bot may send an un-prompted message on this platform. SMS is delivered by the diff --git a/Tests/Resgrid.Tests/Chatbot/ChatbotOutboundTests.cs b/Tests/Resgrid.Tests/Chatbot/ChatbotOutboundTests.cs index 76872a05e..cc925b840 100644 --- a/Tests/Resgrid.Tests/Chatbot/ChatbotOutboundTests.cs +++ b/Tests/Resgrid.Tests/Chatbot/ChatbotOutboundTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; @@ -30,7 +31,7 @@ private static ChatbotOutboundMessage DispatchMsg() => [Test] public void Registry_CanInitiateProactively_ReflectsPlatformConstraints() { - var registry = new ChatbotAdapterRegistry(new List()); + var registry = new ChatbotAdapterRegistry(new Lazy>(() => new List())); registry.CanInitiateProactively(ChatbotPlatform.Slack).Should().BeTrue(); registry.CanInitiateProactively(ChatbotPlatform.WebChat).Should().BeTrue(); @@ -47,12 +48,30 @@ public void Registry_CanInitiateProactively_ReflectsPlatformConstraints() public void Registry_GetAdapter_ResolvesByPlatform() { var slack = new SlackBotAdapter(); - var registry = new ChatbotAdapterRegistry(new List { slack }); + var registry = new ChatbotAdapterRegistry(new Lazy>(() => new List { slack })); registry.GetAdapter(ChatbotPlatform.Slack).Should().BeSameAs(slack); registry.GetAdapter(ChatbotPlatform.Discord).Should().BeNull(); } + [Test] + public void Registry_DoesNotResolveAdaptersUntilFirstGetAdapter() + { + // Guards the circular-dependency fix: constructing the registry (which happens inside the + // CommunicationService -> ChatbotOutboundService activation chain) must not materialize the + // adapters, because WebChatAdapter's dependency graph loops back through CallsService. + var resolved = false; + var registry = new ChatbotAdapterRegistry(new Lazy>(() => + { + resolved = true; + return new List(); + })); + + resolved.Should().BeFalse(); + registry.GetAdapter(ChatbotPlatform.Slack); + resolved.Should().BeTrue(); + } + // ---- ChatbotOutboundService ---- private static (Mock identities, Mock config, Mock registry)