From ef42434d60ecad910daefc96e41cfef1b72ae573 Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Mon, 26 Jan 2026 22:24:24 +0000 Subject: [PATCH] Refactored IocRegistration now compiles and executes properly --- .../appsettings.json | 7 +- e-suite.API/eSuite.API/appsettings.json | 26 +- .../e-suite.Database.Audit/IDatabaseAudit.cs | 11 +- .../e-suite.Database.Core/IRepository.cs | 6 +- .../e-suite.Database.Core/RepositoryBase.cs | 15 +- .../CoreRegistrationModule.cs | 1 - .../ESuiteModuleUnitTests.cs | 59 ++++ .../ModuleDiscoveryUnitTests.cs | 294 ++++++++++++++++++ .../TestRegistration.cs | 13 + ...suite.DependencyInjection.UnitTests.csproj | 21 ++ .../DefaultAssemblyLoader.cs | 8 + e-suite.DependencyInjection/ESuiteModule.cs | 67 ++-- .../EntryAssemblyIdentity.cs | 9 + .../IAssemblyIdentity.cs | 8 + .../IAssemblyLoader.cs | 8 + e-suite.DependencyInjection/IFileSystem.cs | 11 + ...IocRegistration.cs => IIocRegistration.cs} | 0 .../IModuleDiscovery.cs | 8 + e-suite.DependencyInjection/ModuleCache.cs | 7 + .../ModuleDiscovery.cs | 125 ++++++++ e-suite.DependencyInjection/RealFileSystem.cs | 11 + .../e-suite.DependencyInjection.csproj | 4 +- .../CoreRegistrationModule.cs | 27 -- .../e-suite.MessageProcessor/appsettings.json | 24 +- .../e-suite.MessageProcessor.csproj | 86 ++--- .../IocRegistration.cs | 5 +- .../e-suite.Modules.DomainManager.csproj | 1 + .../IocRegistration.cs | 5 +- .../e-suite.Modules.FormsManager.csproj | 1 + .../IocRegistration.cs | 5 +- ...-suite.Modules.MailTemplatesManager.csproj | 1 + .../IocRegistration.cs | 5 +- ...erformanceManager.csproj.nuget.dgspec.json | 4 +- .../obj/project.assets.json | 163 +++++----- .../CoreRegistrationModule.cs | 2 - .../e-suite.Scheduler/appsettings.json | 7 +- .../e-suite.Service.Mail/MailService.cs | 28 +- eSuite.sln | 7 + 38 files changed, 812 insertions(+), 278 deletions(-) create mode 100644 e-suite.DependencyInjection.UnitTests/ESuiteModuleUnitTests.cs create mode 100644 e-suite.DependencyInjection.UnitTests/ModuleDiscoveryUnitTests.cs create mode 100644 e-suite.DependencyInjection.UnitTests/TestRegistration.cs create mode 100644 e-suite.DependencyInjection.UnitTests/e-suite.DependencyInjection.UnitTests.csproj create mode 100644 e-suite.DependencyInjection/DefaultAssemblyLoader.cs create mode 100644 e-suite.DependencyInjection/EntryAssemblyIdentity.cs create mode 100644 e-suite.DependencyInjection/IAssemblyIdentity.cs create mode 100644 e-suite.DependencyInjection/IAssemblyLoader.cs create mode 100644 e-suite.DependencyInjection/IFileSystem.cs rename e-suite.DependencyInjection/{ESuiteIocRegistration.cs => IIocRegistration.cs} (100%) create mode 100644 e-suite.DependencyInjection/IModuleDiscovery.cs create mode 100644 e-suite.DependencyInjection/ModuleCache.cs create mode 100644 e-suite.DependencyInjection/ModuleDiscovery.cs create mode 100644 e-suite.DependencyInjection/RealFileSystem.cs diff --git a/e-suite.API/e-suite.Database.Migrator/appsettings.json b/e-suite.API/e-suite.Database.Migrator/appsettings.json index b4044e8..4d56694 100644 --- a/e-suite.API/e-suite.Database.Migrator/appsettings.json +++ b/e-suite.API/e-suite.Database.Migrator/appsettings.json @@ -5,10 +5,5 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*", - "Modules": [ - "e-suite.API.Common.dll", - "e-suite.Database.Audit.dll", - "e-suite.Database.SqlServer.dll" - ] + "AllowedHosts": "*" } diff --git a/e-suite.API/eSuite.API/appsettings.json b/e-suite.API/eSuite.API/appsettings.json index db8b5f8..d2ba82f 100644 --- a/e-suite.API/eSuite.API/appsettings.json +++ b/e-suite.API/eSuite.API/appsettings.json @@ -16,29 +16,5 @@ } } }, - "AllowedHosts": "*", - "Modules": [ - "e-suite.API.Common.dll", - "e-suite.Database.Audit.dll", - "e-suite.Database.SqlServer.dll", - "e-suite.Modules.BlockedIPsManager.dll", - "e-suite.Messaging.Common.dll", - "e-suite.Modules.AuditLog.dll", - "e-suite.Modules.CustomFieldsManager.dll", - "e-suite.Modules.DomainManager.dll", - "e-suite.Modules.ExceptionLogManager.dll", - "e-suite.Modules.FormsManager.dll", - "e-suite.Modules.GlossariesManager.dll", - "e-suite.Modules.MailTemplatesManager.dll", - "e-suite.Modules.OrganisationsManager.dll", - "e-suite.Modules.RoleManager.dll", - "e-suite.Modules.SequenceManager.dll", - "e-suite.Modules.SiteManager.dll", - "e-suite.Modules.SpecificationManager.dll", - "e-suite.Modules.SSOManager.dll", - "e-suite.Modules.UserManager.dll", - "e-suite.Service.CustomFieldValidation.dll", - "e-suite.Service.Mail.dll", - "e-suite.Service.Sentinel.dll", - ] + "AllowedHosts": "*" } \ No newline at end of file diff --git a/e-suite.Database.Audit/e-suite.Database.Audit/IDatabaseAudit.cs b/e-suite.Database.Audit/e-suite.Database.Audit/IDatabaseAudit.cs index 2b71159..8c119d5 100644 --- a/e-suite.Database.Audit/e-suite.Database.Audit/IDatabaseAudit.cs +++ b/e-suite.Database.Audit/e-suite.Database.Audit/IDatabaseAudit.cs @@ -1,4 +1,6 @@ -namespace e_suite.Database.Audit; +using e_suite.Database.Audit.AuditEngine; + +namespace e_suite.Database.Audit; public interface IDatabaseAudit { @@ -9,4 +11,11 @@ public interface IDatabaseAudit Task NoAuditSaveChangesAsync(); Task NoAuditSaveChangesAsync(CancellationToken cancellationToken); + + Task AddAdhocAuditEntry( + AuditUserDetails auditUserDetails, + AuditType auditType, + Dictionary fields, + CancellationToken cancellationToken + ); } \ No newline at end of file diff --git a/e-suite.Database.Core/e-suite.Database.Core/IRepository.cs b/e-suite.Database.Core/e-suite.Database.Core/IRepository.cs index fc72ee6..c75a18b 100644 --- a/e-suite.Database.Core/e-suite.Database.Core/IRepository.cs +++ b/e-suite.Database.Core/e-suite.Database.Core/IRepository.cs @@ -1,7 +1,11 @@ -namespace e_suite.Database.Core; +using e_suite.Database.Audit; +using e_suite.Database.Audit.AuditEngine; + +namespace e_suite.Database.Core; public interface IRepository { Task TransactionAsync(Func> func); Task TransactionAsync(Func action); + Task AddAdhocAuditEntry( AuditUserDetails auditUserDetails, AuditType auditType, Dictionary fields, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/e-suite.Database.Core/e-suite.Database.Core/RepositoryBase.cs b/e-suite.Database.Core/e-suite.Database.Core/RepositoryBase.cs index 49f5079..4c78475 100644 --- a/e-suite.Database.Core/e-suite.Database.Core/RepositoryBase.cs +++ b/e-suite.Database.Core/e-suite.Database.Core/RepositoryBase.cs @@ -1,4 +1,7 @@ -namespace e_suite.Database.Core; +using e_suite.Database.Audit; +using e_suite.Database.Audit.AuditEngine; + +namespace e_suite.Database.Core; public class RepositoryBase : IRepository { @@ -18,4 +21,14 @@ public class RepositoryBase : IRepository { await DatabaseDbContext.TransactionAsync(action); } + + public async Task AddAdhocAuditEntry( + AuditUserDetails auditUserDetails, + AuditType auditType, + Dictionary fields, + CancellationToken cancellationToken + ) + { + return await DatabaseDbContext.AddAdhocAuditEntry(auditUserDetails, auditType, fields, cancellationToken); + } } \ No newline at end of file diff --git a/e-suite.Database.SqlServer/e-suite.Database.MigrationBuilder/DependencyInjection/CoreRegistrationModule.cs b/e-suite.Database.SqlServer/e-suite.Database.MigrationBuilder/DependencyInjection/CoreRegistrationModule.cs index 05326f2..a4f585d 100644 --- a/e-suite.Database.SqlServer/e-suite.Database.MigrationBuilder/DependencyInjection/CoreRegistrationModule.cs +++ b/e-suite.Database.SqlServer/e-suite.Database.MigrationBuilder/DependencyInjection/CoreRegistrationModule.cs @@ -19,6 +19,5 @@ internal class CoreRegistrationModule : ESuiteModule base.Load(builder); builder.RegisterType().As().InstancePerLifetimeScope(); - //builder.RegisterType().As().SingleInstance(); } } \ No newline at end of file diff --git a/e-suite.DependencyInjection.UnitTests/ESuiteModuleUnitTests.cs b/e-suite.DependencyInjection.UnitTests/ESuiteModuleUnitTests.cs new file mode 100644 index 0000000..b674b3e --- /dev/null +++ b/e-suite.DependencyInjection.UnitTests/ESuiteModuleUnitTests.cs @@ -0,0 +1,59 @@ +using Autofac; +using eSuite.Core.Clock; +using Moq; +using NUnit.Framework; + +namespace e_suite.DependencyInjection.UnitTests; + +[TestFixture] +public class ESuiteModuleUnitTests +{ + [Test] + public void Load_CallsModuleDiscovery() + { + // Arrange + var mockDiscovery = new Mock(); + var module = new ESuiteModule(mockDiscovery.Object); + + ContainerBuilder? capturedBuilder = null; + + mockDiscovery + .Setup(d => d.RegisterAllModuleIocRegistrations(It.IsAny())) + .Callback(b => capturedBuilder = b); + + var builder = new ContainerBuilder(); + builder.RegisterModule(module); + + // Act + builder.Build(); + + // Assert + mockDiscovery.Verify( + d => d.RegisterAllModuleIocRegistrations(It.IsAny()), + Times.Once); + + Assert.That(capturedBuilder, Is.Not.Null); + } + + [Test] + public void Load_RegistersUtcClockAsIClock() + { + // Arrange + var mockDiscovery = new Mock(); + // Don’t let discovery do anything in this test + mockDiscovery + .Setup(d => d.RegisterAllModuleIocRegistrations(It.IsAny())); + + var module = new ESuiteModule(mockDiscovery.Object); + + var builder = new ContainerBuilder(); + builder.RegisterModule(module); + + // Act + var container = builder.Build(); + var clock = container.Resolve(); + + // Assert + Assert.That(clock, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/e-suite.DependencyInjection.UnitTests/ModuleDiscoveryUnitTests.cs b/e-suite.DependencyInjection.UnitTests/ModuleDiscoveryUnitTests.cs new file mode 100644 index 0000000..1686de4 --- /dev/null +++ b/e-suite.DependencyInjection.UnitTests/ModuleDiscoveryUnitTests.cs @@ -0,0 +1,294 @@ +using System.Security.Cryptography; +using Autofac; +using Moq; +using NUnit.Framework; + +namespace e_suite.DependencyInjection.UnitTests; + +[TestFixture] +public class ModuleDiscoveryUnitTests +{ + [Test] + public void LoadOrCreateModuleCache_UsesExistingCache_WhenHashMatches() + { + // Arrange + var fileSystem = new Mock(); + var assemblyLoader = new Mock(); + var identity = new Mock(); + + var basePath = AppContext.BaseDirectory; + var cacheDirectory = Path.Combine(basePath, ".cache"); + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + // The hash we expect the discovery code to compute + var fakeBytes = new byte[] { 1, 2, 3, 4 }; + var expectedHash = Convert.ToHexString(SHA256.HashData(fakeBytes)); + + // Existing cache with the SAME hash + var existingCacheJson = $$""" + { + "AssemblyHash": "{{expectedHash}}", + "Modules": [ "TestModule.dll" ] + } + """; + + // Mock filesystem behaviour + fileSystem.Setup(fs => fs.CreateDirectory(cacheDirectory)); + fileSystem.Setup(fs => fs.FileExists(cachePath)).Returns(true); + fileSystem.Setup(fs => fs.FileExists(Path.Combine(basePath, "TestModule.dll"))).Returns(true); + fileSystem.Setup(fs => fs.ReadAllText(cachePath)).Returns(existingCacheJson); + + + // Mock assembly identity → returns an assembly whose hash we control + var assembly = typeof(ModuleDiscoveryUnitTests).Assembly; + identity.Setup(x => x.GetAssemblyForHashing()).Returns(assembly); + + // Mock hashing stream + // The bytes here don't matter — the test only cares that the hash matches + fileSystem.Setup(fs => fs.OpenRead(assembly.Location)) + .Returns(new MemoryStream(fakeBytes)); + + // Mock assembly loader for the module listed in the cache + assemblyLoader.Setup(l => l.LoadFrom(Path.Combine(basePath, "TestModule.dll"))) + .Returns(assembly); + + var discovery = new ModuleDiscovery(fileSystem.Object, assemblyLoader.Object, identity.Object); + + // Act + discovery.RegisterAllModuleIocRegistrations(new ContainerBuilder()); + + // Assert: cache should NOT be rewritten + fileSystem.Verify(fs => fs.WriteAllText(It.IsAny(), It.IsAny()), Times.Never); + + // Assert: existing cache was read + fileSystem.Verify(fs => fs.ReadAllText(cachePath), Times.Once); + } + + [Test] + public void LoadOrCreateModuleCache_RegeneratesCache_WhenHashDoesNotMatch() + { + // Arrange + var fileSystem = new Mock(); + var assemblyLoader = new Mock(); + var identity = new Mock(); + + var basePath = AppContext.BaseDirectory; + var cacheDirectory = Path.Combine(basePath, ".cache"); + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + var existingCacheJson = """ + { + "AssemblyHash": "OLDHASH", + "Modules": [ "OldModule.dll" ] + } + """; + + fileSystem.Setup(fs => fs.CreateDirectory(cacheDirectory)); + fileSystem.Setup(fs => fs.FileExists(cachePath)).Returns(true); + fileSystem.Setup(fs => fs.ReadAllText(cachePath)).Returns(existingCacheJson); + + string? writtenJson = null; + fileSystem.Setup(fs => fs.WriteAllText(cachePath, It.IsAny())) + .Callback((_, json) => writtenJson = json); + + var assembly = typeof(TestRegistration).Assembly; + identity.Setup(x => x.GetAssemblyForHashing()).Returns(assembly); + + var fakeStream = new MemoryStream(new byte[] { 1, 2, 3, 4 }); + fileSystem.Setup(fs => fs.OpenRead(assembly.Location)).Returns(fakeStream); + + var newModulePath = Path.Combine(basePath, "NewModule.dll"); + + // Regeneration scan + fileSystem.Setup(fs => fs.GetFiles(basePath, "*.dll")) + .Returns(new[] { newModulePath }); + + // After regeneration, RegisterAllModuleIocRegistrations will check FileExists on fullPath + fileSystem.Setup(fs => fs.FileExists(newModulePath)).Returns(true); + + assemblyLoader.Setup(l => l.LoadFrom(newModulePath)) + .Returns(typeof(TestRegistration).Assembly); + + var discovery = new ModuleDiscovery(fileSystem.Object, assemblyLoader.Object, identity.Object); + + // Act + discovery.RegisterAllModuleIocRegistrations(new ContainerBuilder()); + + // Assert + fileSystem.Verify(fs => fs.WriteAllText(cachePath, It.IsAny()), Times.Once); + fileSystem.Verify(fs => fs.GetFiles(basePath, "*.dll"), Times.Once); + + Assert.That(writtenJson, Is.Not.Null); + Assert.That(writtenJson, Does.Contain("NewModule.dll")); + } + + [Test] + public void RegisterAllModuleIocRegistrations_Throws_WhenCachedModuleMissing() + { + // Arrange + var fileSystem = new Mock(); + var assemblyLoader = new Mock(); + var identity = new Mock(); + + var basePath = AppContext.BaseDirectory; + var cacheDirectory = Path.Combine(basePath, ".cache"); + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + // Fake bytes for hashing + var fakeBytes = new byte[] { 1, 2, 3, 4 }; + var expectedHash = Convert.ToHexString(SHA256.HashData(fakeBytes)); + + // Cache contains a module that does NOT exist + var existingCacheJson = $$""" + { + "AssemblyHash": "{{expectedHash}}", + "Modules": [ "MissingModule.dll" ] + } + """; + + // Mock filesystem + fileSystem.Setup(fs => fs.CreateDirectory(cacheDirectory)); + fileSystem.Setup(fs => fs.FileExists(cachePath)).Returns(true); + fileSystem.Setup(fs => fs.ReadAllText(cachePath)).Returns(existingCacheJson); + + // Hashing stream + var assembly = typeof(ModuleDiscoveryUnitTests).Assembly; + identity.Setup(x => x.GetAssemblyForHashing()).Returns(assembly); + fileSystem.Setup(fs => fs.OpenRead(assembly.Location)) + .Returns(new MemoryStream(fakeBytes)); + + // Critical: Missing module file → FileExists returns false + var missingModulePath = Path.Combine(basePath, "MissingModule.dll"); + fileSystem.Setup(fs => fs.FileExists(missingModulePath)).Returns(false); + + var discovery = new ModuleDiscovery(fileSystem.Object, assemblyLoader.Object, identity.Object); + + // Act + Assert + var ex = Assert.Throws(() => + discovery.RegisterAllModuleIocRegistrations(new ContainerBuilder())); + + Assert.That(ex!.Message, Does.Contain("MissingModule.dll")); + } + + [Test] + public void RegenerateModuleCache_IgnoresAssemblyLoadFailures() + { + // Arrange + var fileSystem = new Mock(); + var assemblyLoader = new Mock(); + var identity = new Mock(); + + var basePath = AppContext.BaseDirectory; + var cacheDirectory = Path.Combine(basePath, ".cache"); + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + // Existing cache with WRONG hash → forces regeneration + var existingCacheJson = """ + { + "AssemblyHash": "OLDHASH", + "Modules": [ "OldModule.dll" ] + } + """; + + fileSystem.Setup(fs => fs.CreateDirectory(cacheDirectory)); + fileSystem.Setup(fs => fs.FileExists(cachePath)).Returns(true); + fileSystem.Setup(fs => fs.ReadAllText(cachePath)).Returns(existingCacheJson); + + // Capture regenerated JSON + string? writtenJson = null; + fileSystem.Setup(fs => fs.WriteAllText(cachePath, It.IsAny())) + .Callback((_, json) => writtenJson = json); + + // Hashing setup + var assembly = typeof(TestRegistration).Assembly; + identity.Setup(x => x.GetAssemblyForHashing()).Returns(assembly); + + var fakeStream = new MemoryStream(new byte[] { 1, 2, 3, 4 }); + fileSystem.Setup(fs => fs.OpenRead(assembly.Location)).Returns(fakeStream); + + // Regeneration scan finds a DLL + var badDllPath = Path.Combine(basePath, "BadModule.dll"); + fileSystem.Setup(fs => fs.GetFiles(basePath, "*.dll")) + .Returns(new[] { badDllPath }); + + // Critical: assembly load fails + assemblyLoader.Setup(l => l.LoadFrom(badDllPath)) + .Throws(new BadImageFormatException("Not a .NET assembly")); + + // After regeneration, RegisterAllModuleIocRegistrations will check FileExists + // but since no modules are added, this path is never hit. + // Still safe to mock it as false. + fileSystem.Setup(fs => fs.FileExists(It.IsAny())).Returns(false); + + var discovery = new ModuleDiscovery(fileSystem.Object, assemblyLoader.Object, identity.Object); + + // Act + discovery.RegisterAllModuleIocRegistrations(new ContainerBuilder()); + + // Assert: regeneration happened + fileSystem.Verify(fs => fs.GetFiles(basePath, "*.dll"), Times.Once); + + // Assert: cache was rewritten + fileSystem.Verify(fs => fs.WriteAllText(cachePath, It.IsAny()), Times.Once); + + // Assert: regenerated cache contains NO modules + Assert.That(writtenJson, Is.Not.Null); + Assert.That(writtenJson, Does.Not.Contain("BadModule.dll")); + } + + [Test] + public void RegisterAllModuleIocRegistrations_InvokesRegistrationTypes() + { + // Arrange + TestRegistration.WasCalled = false; + + var fileSystem = new Mock(); + var assemblyLoader = new Mock(); + var identity = new Mock(); + + var basePath = AppContext.BaseDirectory; + var cacheDirectory = Path.Combine(basePath, ".cache"); + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + // Fake hash bytes + var fakeBytes = new byte[] { 1, 2, 3, 4 }; + var expectedHash = Convert.ToHexString(SHA256.HashData(fakeBytes)); + + // Cache contains our test module + var existingCacheJson = $$""" + { + "AssemblyHash": "{{expectedHash}}", + "Modules": [ "TestModule.dll" ] + } + """; + + // Filesystem mocks + fileSystem.Setup(fs => fs.CreateDirectory(cacheDirectory)); + fileSystem.Setup(fs => fs.FileExists(cachePath)).Returns(true); + fileSystem.Setup(fs => fs.ReadAllText(cachePath)).Returns(existingCacheJson); + + // Hashing stream + var assembly = typeof(TestRegistration).Assembly; + identity.Setup(x => x.GetAssemblyForHashing()).Returns(assembly); + fileSystem.Setup(fs => fs.OpenRead(assembly.Location)) + .Returns(new MemoryStream(fakeBytes)); + + // Module file exists + var modulePath = Path.Combine(basePath, "TestModule.dll"); + fileSystem.Setup(fs => fs.FileExists(modulePath)).Returns(true); + + // Assembly loader returns assembly containing TestRegistration + assemblyLoader.Setup(l => l.LoadFrom(modulePath)) + .Returns(typeof(TestRegistration).Assembly); + + var discovery = new ModuleDiscovery(fileSystem.Object, assemblyLoader.Object, identity.Object); + + var builder = new ContainerBuilder(); + + // Act + discovery.RegisterAllModuleIocRegistrations(builder); + + // Assert + Assert.That(TestRegistration.WasCalled, Is.True); + } +} \ No newline at end of file diff --git a/e-suite.DependencyInjection.UnitTests/TestRegistration.cs b/e-suite.DependencyInjection.UnitTests/TestRegistration.cs new file mode 100644 index 0000000..049a30d --- /dev/null +++ b/e-suite.DependencyInjection.UnitTests/TestRegistration.cs @@ -0,0 +1,13 @@ +using Autofac; + +namespace e_suite.DependencyInjection.UnitTests; + +public class TestRegistration : IIocRegistration +{ + public static bool WasCalled; + + public void RegisterTypes(ContainerBuilder builder) + { + WasCalled = true; + } +} \ No newline at end of file diff --git a/e-suite.DependencyInjection.UnitTests/e-suite.DependencyInjection.UnitTests.csproj b/e-suite.DependencyInjection.UnitTests/e-suite.DependencyInjection.UnitTests.csproj new file mode 100644 index 0000000..97f3659 --- /dev/null +++ b/e-suite.DependencyInjection.UnitTests/e-suite.DependencyInjection.UnitTests.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + e_suite.DependencyInjection.UnitTests + enable + enable + + + + + + + + + + + + + + diff --git a/e-suite.DependencyInjection/DefaultAssemblyLoader.cs b/e-suite.DependencyInjection/DefaultAssemblyLoader.cs new file mode 100644 index 0000000..0985104 --- /dev/null +++ b/e-suite.DependencyInjection/DefaultAssemblyLoader.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace e_suite.DependencyInjection; + +public class DefaultAssemblyLoader : IAssemblyLoader +{ + public Assembly LoadFrom(string path) => Assembly.LoadFrom(path); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/ESuiteModule.cs b/e-suite.DependencyInjection/ESuiteModule.cs index f130abb..2c63111 100644 --- a/e-suite.DependencyInjection/ESuiteModule.cs +++ b/e-suite.DependencyInjection/ESuiteModule.cs @@ -1,59 +1,34 @@ using Autofac; using eSuite.Core.Clock; -using Microsoft.Extensions.Configuration; - -using System.Reflection; namespace e_suite.DependencyInjection; -public sealed class ModuleConfig -{ - public List Modules { get; set; } = new(); -} - public class ESuiteModule : Autofac.Module { + private readonly IModuleDiscovery _moduleDiscovery; + + public ESuiteModule() + : this(CreateDefaultDiscovery()) + { + } + + public ESuiteModule(IModuleDiscovery moduleDiscovery) + { + _moduleDiscovery = moduleDiscovery; + } + + private static IModuleDiscovery CreateDefaultDiscovery() => + new ModuleDiscovery( + new RealFileSystem(), + new DefaultAssemblyLoader(), + new EntryAssemblyIdentity() + ); + protected override void Load(ContainerBuilder builder) { base.Load(builder); builder.RegisterType().As().SingleInstance(); - - RegisterAllModuleIocRegistrations(builder); + + _moduleDiscovery.RegisterAllModuleIocRegistrations(builder); } - - protected static void RegisterAllModuleIocRegistrations(ContainerBuilder builder) - { - var configuration = new ConfigurationBuilder() - .SetBasePath(AppContext.BaseDirectory) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) - .Build(); - - var moduleConfig = configuration.Get(); - - var basePath = AppContext.BaseDirectory; - var assemblies = new List(); - - foreach (var module in moduleConfig.Modules) - { - var fullPath = Path.Combine(basePath, module); - - if (!File.Exists(fullPath)) - throw new FileNotFoundException($"Configured module not found: {fullPath}"); - - assemblies.Add(Assembly.LoadFrom(fullPath)); - } - - var registrationTypes = assemblies - .SelectMany(a => a.GetTypes()) - .Where(t => typeof(IIocRegistration).IsAssignableFrom(t) - && !t.IsAbstract - && !t.IsInterface).ToList(); - - foreach (var type in registrationTypes) - { - var instance = (IIocRegistration)Activator.CreateInstance(type)!; - instance.RegisterTypes(builder); - } - } - } \ No newline at end of file diff --git a/e-suite.DependencyInjection/EntryAssemblyIdentity.cs b/e-suite.DependencyInjection/EntryAssemblyIdentity.cs new file mode 100644 index 0000000..5196f6b --- /dev/null +++ b/e-suite.DependencyInjection/EntryAssemblyIdentity.cs @@ -0,0 +1,9 @@ +using System.Reflection; + +namespace e_suite.DependencyInjection; + +public class EntryAssemblyIdentity : IAssemblyIdentity +{ + public Assembly GetAssemblyForHashing() => + Assembly.GetEntryAssembly()!; +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/IAssemblyIdentity.cs b/e-suite.DependencyInjection/IAssemblyIdentity.cs new file mode 100644 index 0000000..2de7190 --- /dev/null +++ b/e-suite.DependencyInjection/IAssemblyIdentity.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace e_suite.DependencyInjection; + +public interface IAssemblyIdentity +{ + Assembly GetAssemblyForHashing(); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/IAssemblyLoader.cs b/e-suite.DependencyInjection/IAssemblyLoader.cs new file mode 100644 index 0000000..26563fd --- /dev/null +++ b/e-suite.DependencyInjection/IAssemblyLoader.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace e_suite.DependencyInjection; + +public interface IAssemblyLoader +{ + Assembly LoadFrom(string path); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/IFileSystem.cs b/e-suite.DependencyInjection/IFileSystem.cs new file mode 100644 index 0000000..56c6d0c --- /dev/null +++ b/e-suite.DependencyInjection/IFileSystem.cs @@ -0,0 +1,11 @@ +namespace e_suite.DependencyInjection; + +public interface IFileSystem +{ + bool FileExists(string path); + void CreateDirectory(string path); + string[] GetFiles(string path, string searchPattern); + string ReadAllText(string path); + void WriteAllText(string path, string contents); + Stream OpenRead(string path); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/ESuiteIocRegistration.cs b/e-suite.DependencyInjection/IIocRegistration.cs similarity index 100% rename from e-suite.DependencyInjection/ESuiteIocRegistration.cs rename to e-suite.DependencyInjection/IIocRegistration.cs diff --git a/e-suite.DependencyInjection/IModuleDiscovery.cs b/e-suite.DependencyInjection/IModuleDiscovery.cs new file mode 100644 index 0000000..815a1dd --- /dev/null +++ b/e-suite.DependencyInjection/IModuleDiscovery.cs @@ -0,0 +1,8 @@ +using Autofac; + +namespace e_suite.DependencyInjection; + +public interface IModuleDiscovery +{ + void RegisterAllModuleIocRegistrations(ContainerBuilder builder); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/ModuleCache.cs b/e-suite.DependencyInjection/ModuleCache.cs new file mode 100644 index 0000000..a062785 --- /dev/null +++ b/e-suite.DependencyInjection/ModuleCache.cs @@ -0,0 +1,7 @@ +namespace e_suite.DependencyInjection; + +public sealed class ModuleCache +{ + public string AssemblyHash { get; set; } = string.Empty; + public List Modules { get; set; } = new(); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/ModuleDiscovery.cs b/e-suite.DependencyInjection/ModuleDiscovery.cs new file mode 100644 index 0000000..13fa353 --- /dev/null +++ b/e-suite.DependencyInjection/ModuleDiscovery.cs @@ -0,0 +1,125 @@ +using System.Reflection; +using Autofac; + +namespace e_suite.DependencyInjection; + +public class ModuleDiscovery : IModuleDiscovery +{ + private readonly IFileSystem _fileSystem; + private readonly IAssemblyLoader _assemblyLoader; + private readonly IAssemblyIdentity _assemblyIdentity; + + public ModuleDiscovery(IFileSystem fileSystem, IAssemblyLoader assemblyLoader, IAssemblyIdentity assemblyIdentity) + { + _fileSystem = fileSystem; + _assemblyLoader = assemblyLoader; + _assemblyIdentity = assemblyIdentity; + } + + public void RegisterAllModuleIocRegistrations(ContainerBuilder builder) + { + var cacheDirectory = Path.Combine(AppContext.BaseDirectory, ".cache"); + _fileSystem.CreateDirectory(cacheDirectory); + + var cachePath = Path.Combine(cacheDirectory, "module-cache.json"); + + var cache = LoadOrCreateModuleCache(cachePath); + + var basePath = AppContext.BaseDirectory; + var assemblies = new List(); + + foreach (var module in cache.Modules) + { + var fullPath = Path.Combine(basePath, module); + + if (!_fileSystem.FileExists(fullPath)) + throw new FileNotFoundException($"Cached module not found: {fullPath}"); + + assemblies.Add(_assemblyLoader.LoadFrom(fullPath)); + } + + var registrationTypes = assemblies + .SelectMany(a => a.GetTypes()) + .Where(t => typeof(IIocRegistration).IsAssignableFrom(t) + && t is { IsAbstract: false, IsInterface: false }) + .ToList(); + + foreach (var type in registrationTypes) + { + var instance = (IIocRegistration)Activator.CreateInstance(type)!; + instance.RegisterTypes(builder); + } + } + + private ModuleCache LoadOrCreateModuleCache(string cachePath) + { + var currentHash = ComputeAssemblyHash(); + + if (_fileSystem.FileExists(cachePath)) + { + var json = _fileSystem.ReadAllText(cachePath); + var cache = System.Text.Json.JsonSerializer.Deserialize(json); + + if (cache is not null && cache.AssemblyHash == currentHash) + return cache; + } + + // Cache missing or invalid → regenerate + var regenerated = RegenerateModuleCache(currentHash); + + var output = System.Text.Json.JsonSerializer.Serialize(regenerated, new System.Text.Json.JsonSerializerOptions + { + WriteIndented = true + }); + + _fileSystem.WriteAllText(cachePath, output); + + return regenerated; + } + + private ModuleCache RegenerateModuleCache(string assemblyHash) + { + var basePath = AppContext.BaseDirectory; + var dlls = _fileSystem.GetFiles(basePath, "*.dll"); + + var modules = new List(); + + foreach (var dll in dlls) + { + try + { + var asm = _assemblyLoader.LoadFrom(dll); + + var hasRegistration = asm.GetTypes().Any(t => + typeof(IIocRegistration).IsAssignableFrom(t) && + !t.IsAbstract && + !t.IsInterface); + + if (hasRegistration) + modules.Add(Path.GetFileName(dll)); + } + catch + { + // Ignore non-.NET DLLs or load failures + } + } + + return new ModuleCache + { + AssemblyHash = assemblyHash, + Modules = modules + }; + } + + private string ComputeAssemblyHash() + { + var mainAssembly = _assemblyIdentity.GetAssemblyForHashing(); + var path = mainAssembly.Location; + + using var stream = _fileSystem.OpenRead(path); + using var sha = System.Security.Cryptography.SHA256.Create(); + + var hash = sha.ComputeHash(stream); + return Convert.ToHexString(hash); + } +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/RealFileSystem.cs b/e-suite.DependencyInjection/RealFileSystem.cs new file mode 100644 index 0000000..b372105 --- /dev/null +++ b/e-suite.DependencyInjection/RealFileSystem.cs @@ -0,0 +1,11 @@ +namespace e_suite.DependencyInjection; + +public class RealFileSystem : IFileSystem +{ + public bool FileExists(string path) => File.Exists(path); + public void CreateDirectory(string path) => Directory.CreateDirectory(path); + public string[] GetFiles(string path, string searchPattern) => Directory.GetFiles(path, searchPattern); + public string ReadAllText(string path) => File.ReadAllText(path); + public void WriteAllText(string path, string contents) => File.WriteAllText(path, contents); + public Stream OpenRead(string path) => File.OpenRead(path); +} \ No newline at end of file diff --git a/e-suite.DependencyInjection/e-suite.DependencyInjection.csproj b/e-suite.DependencyInjection/e-suite.DependencyInjection.csproj index a28f707..6bd3f32 100644 --- a/e-suite.DependencyInjection/e-suite.DependencyInjection.csproj +++ b/e-suite.DependencyInjection/e-suite.DependencyInjection.csproj @@ -16,7 +16,7 @@ - - + + diff --git a/e-suite.MessageProcessor/e-suite.MessageProcessor/DependencyInjection/CoreRegistrationModule.cs b/e-suite.MessageProcessor/e-suite.MessageProcessor/DependencyInjection/CoreRegistrationModule.cs index 8065107..364a5c0 100644 --- a/e-suite.MessageProcessor/e-suite.MessageProcessor/DependencyInjection/CoreRegistrationModule.cs +++ b/e-suite.MessageProcessor/e-suite.MessageProcessor/DependencyInjection/CoreRegistrationModule.cs @@ -26,9 +26,6 @@ public class CoreRegistrationModule : ESuiteModule var loggerFactory = LoggerFactory.Create(loggingBuilder => loggingBuilder.AddConsole()); builder.Register(x => loggerFactory.CreateLogger("e-Suite.MessageProcessor")).As().SingleInstance(); - //IClock clock = new UtcClock(); - //builder.RegisterInstance(clock).As().SingleInstance(); - builder.RegisterInstance(ESuiteDatabaseExtension.BuildConfiguration()).As().SingleInstance(); builder.Register(c => { @@ -40,33 +37,9 @@ public class CoreRegistrationModule : ESuiteModule .As() .InstancePerLifetimeScope(); - - //builder.Register(c => ESuiteDatabaseExtension.CreateDatabase(clock).GetAwaiter().GetResult()) - // .As() - // .InstancePerLifetimeScope(); - builder.RegisterType().InstancePerLifetimeScope(); builder.RegisterType().InstancePerLifetimeScope(); builder.RegisterType().InstancePerLifetimeScope(); builder.RegisterType().InstancePerLifetimeScope(); - - //Service.Sentinel.IocRegistration.RegisterTypes(builder); - //Service.Performance.IocRegistration.RegisterTypes(builder); - //Modules.UserManager.IocRegistration.RegisterTypes(builder); - //Messaging.Common.DependencyInjection.CoreRegistrationModule.Load(builder); - //Modules.ExceptionLogManager.IocRegistration.RegisterTypes(builder); - //Service.SigmaImporter.IocRegistration.RegisterTypes(builder); - //Modules.GlossariesManager.IocRegistration.RegisterTypes(builder); - //Modules.DomainManager.IocRegistration.RegisterTypes(builder); - //Modules.CustomFieldsManager.IocRegistration.RegisterTypes(builder); - //Service.CustomFieldValidation.IocRegistration.RegisterTypes(builder); - //Modules.SequenceManager.IocRegistration.RegisterTypes(builder); - //Modules.FormsManager.IocRegistration.RegisterTypes(builder); - //Modules.SiteManager.IocRegistration.RegisterTypes(builder); - //Modules.SpecificationManager.IocRegistration.RegisterTypes(builder); - //Modules.OrganisationsManager.IocRegistration.RegisterTypes(builder); - //Modules.DomainManager.IocRegistration.RegisterTypes(builder); - //Modules.SSOManager.IocRegistration.RegisterTypes(builder); - //Service.EFlowSync.IocRegistration.RegisterTypes(builder); } } \ No newline at end of file diff --git a/e-suite.MessageProcessor/e-suite.MessageProcessor/appsettings.json b/e-suite.MessageProcessor/e-suite.MessageProcessor/appsettings.json index e3f496d..2b6655d 100644 --- a/e-suite.MessageProcessor/e-suite.MessageProcessor/appsettings.json +++ b/e-suite.MessageProcessor/e-suite.MessageProcessor/appsettings.json @@ -28,27 +28,5 @@ }, "EflowAPI": { "Server": "https://sunrise-test-api-we.azurewebsites.net/" - }, - "Modules": [ - "e-suite.API.Common.dll", - "e-suite.Database.Audit.dll", - "e-suite.Database.SqlServer.dll", - "e-suite.Messaging.Common.dll", - "e-suite.Modules.CustomFieldsManager.dll", - "e-suite.Modules.DomainManager.dll", - "e-suite.Modules.ExceptionLogManager.dll", - "e-suite.Modules.FormsManager.dll", - "e-suite.Modules.GlossariesManager.dll", - "e-suite.Modules.OrganisationsManager.dll", - "e-suite.Modules.SequenceManager.dll", - "e-suite.Modules.SiteManager.dll", - "e-suite.Modules.SpecificationManager.dll", - "e-suite.Modules.SSOManager.dll", - "e-suite.Modules.UserManager.dll", - "e-suite.Service.CustomFieldValidation.dll", - "e-suite.Service.EFlowSync.dll", - "e-suite.Service.Performance.dll", - "e-suite.Service.Sentinel.dll", - "e-suite.Service.SigmaImporter.dll" - ] + } } \ No newline at end of file diff --git a/e-suite.MessageProcessor/e-suite.MessageProcessor/e-suite.MessageProcessor.csproj b/e-suite.MessageProcessor/e-suite.MessageProcessor/e-suite.MessageProcessor.csproj index 88edcc2..6654d1f 100644 --- a/e-suite.MessageProcessor/e-suite.MessageProcessor/e-suite.MessageProcessor.csproj +++ b/e-suite.MessageProcessor/e-suite.MessageProcessor/e-suite.MessageProcessor.csproj @@ -1,37 +1,37 @@ - - - - Exe - net10.0 - e_suite.MessageProcessor - enable - enable - - - - - - - - - - - Always - - - Always - - - Always - - - - - - - - - + + + + Exe + net10.0 + e_suite.MessageProcessor + enable + enable + + + + + + + + + + + Always + + + Always + + + Always + + + + + + + + + @@ -54,12 +54,12 @@ - - - - - PreserveNewest - - - - + + + + + PreserveNewest + + + + diff --git a/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/IocRegistration.cs b/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/IocRegistration.cs index fff7f5d..38bab8b 100644 --- a/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/IocRegistration.cs +++ b/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/IocRegistration.cs @@ -1,13 +1,14 @@ using Autofac; using e_suite.API.Common; using e_suite.API.Common.repository; +using e_suite.DependencyInjection; using e_suite.Modules.DomainManager.Repository; namespace e_suite.Modules.DomainManager; -public class IocRegistration +public class IocRegistration : IIocRegistration { - public static void RegisterTypes(ContainerBuilder builder) + public void RegisterTypes(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); diff --git a/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager.csproj b/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager.csproj index 8ab8bb5..c0fcf16 100644 --- a/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager.csproj +++ b/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager/e-suite.Modules.DomainManager.csproj @@ -13,5 +13,6 @@ + diff --git a/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/IocRegistration.cs b/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/IocRegistration.cs index 8c2a495..897b90d 100644 --- a/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/IocRegistration.cs +++ b/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/IocRegistration.cs @@ -1,13 +1,14 @@ using Autofac; using e_suite.API.Common; using e_suite.API.Common.repository; +using e_suite.DependencyInjection; using e_suite.Modules.FormsManager.Repository; namespace e_suite.Modules.FormsManager; -public class IocRegistration +public class IocRegistration : IIocRegistration { - public static void RegisterTypes(ContainerBuilder builder) + public void RegisterTypes(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); diff --git a/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager.csproj b/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager.csproj index e894619..23f034d 100644 --- a/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager.csproj +++ b/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager/e-suite.Modules.FormsManager.csproj @@ -14,6 +14,7 @@ + diff --git a/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/IocRegistration.cs b/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/IocRegistration.cs index 39fafc8..4b793dd 100644 --- a/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/IocRegistration.cs +++ b/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/IocRegistration.cs @@ -1,13 +1,14 @@ using Autofac; using e_suite.API.Common; using e_suite.API.Common.repository; +using e_suite.DependencyInjection; using e_suite.Modules.MailTemplatesManager.repository; namespace e_suite.Modules.MailTemplatesManager; -public class IocRegistration +public class IocRegistration : IIocRegistration { - public static void RegisterTypes(ContainerBuilder builder) + public void RegisterTypes(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); diff --git a/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager.csproj b/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager.csproj index daa446d..8ab7ff1 100644 --- a/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager.csproj +++ b/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager/e-suite.Modules.MailTemplatesManager.csproj @@ -14,6 +14,7 @@ + diff --git a/e-suite.Modules.OrganisationManager/e-Suite.Modules.OrganisationsManager/IocRegistration.cs b/e-suite.Modules.OrganisationManager/e-Suite.Modules.OrganisationsManager/IocRegistration.cs index d3b8199..8f026cd 100644 --- a/e-suite.Modules.OrganisationManager/e-Suite.Modules.OrganisationsManager/IocRegistration.cs +++ b/e-suite.Modules.OrganisationManager/e-Suite.Modules.OrganisationsManager/IocRegistration.cs @@ -1,13 +1,14 @@ using Autofac; using e_suite.API.Common; using e_suite.API.Common.repository; +using e_suite.DependencyInjection; using e_suite.Modules.OrganisationsManager.Repository; namespace e_suite.Modules.OrganisationsManager; -public class IocRegistration +public class IocRegistration : IIocRegistration { - public static void RegisterTypes(ContainerBuilder builder) + public void RegisterTypes(ContainerBuilder builder) { builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); diff --git a/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/e-suite.Modules.PerformanceManager.csproj.nuget.dgspec.json b/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/e-suite.Modules.PerformanceManager.csproj.nuget.dgspec.json index edfd0c1..11acce9 100644 --- a/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/e-suite.Modules.PerformanceManager.csproj.nuget.dgspec.json +++ b/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/e-suite.Modules.PerformanceManager.csproj.nuget.dgspec.json @@ -1475,11 +1475,11 @@ }, "Microsoft.Extensions.Configuration.FileExtensions": { "target": "Package", - "version": "[8.0.0, )" + "version": "[10.0.2, )" }, "Microsoft.Extensions.Configuration.Json": { "target": "Package", - "version": "[8.0.0, )" + "version": "[10.0.2, )" } }, "imports": [ diff --git a/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/project.assets.json b/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/project.assets.json index f961baa..e213b92 100644 --- a/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/project.assets.json +++ b/e-suite.Modules.PerformanceManager/e-suite.Modules.PerformanceManager/obj/project.assets.json @@ -154,49 +154,49 @@ "buildTransitive/netstandard2.0/Microsoft.Extensions.Configuration.Binder.targets": {} } }, - "Microsoft.Extensions.Configuration.FileExtensions/8.0.0": { + "Microsoft.Extensions.Configuration.FileExtensions/10.0.2": { "type": "package", "dependencies": { - "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.FileProviders.Physical": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Physical": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" }, "compile": { - "lib/net8.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { + "lib/net10.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { "related": ".xml" } }, "runtime": { - "lib/net8.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { + "lib/net10.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { "related": ".xml" } }, "build": { - "buildTransitive/net6.0/_._": {} + "buildTransitive/net8.0/_._": {} } }, - "Microsoft.Extensions.Configuration.Json/8.0.0": { + "Microsoft.Extensions.Configuration.Json/10.0.2": { "type": "package", "dependencies": { - "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2" }, "compile": { - "lib/net8.0/Microsoft.Extensions.Configuration.Json.dll": { + "lib/net10.0/Microsoft.Extensions.Configuration.Json.dll": { "related": ".xml" } }, "runtime": { - "lib/net8.0/Microsoft.Extensions.Configuration.Json.dll": { + "lib/net10.0/Microsoft.Extensions.Configuration.Json.dll": { "related": ".xml" } }, "build": { - "buildTransitive/net6.0/_._": {} + "buildTransitive/net8.0/_._": {} } }, "Microsoft.Extensions.DependencyInjection/10.0.2": { @@ -234,60 +234,60 @@ "buildTransitive/net8.0/_._": {} } }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { + "Microsoft.Extensions.FileProviders.Abstractions/10.0.2": { "type": "package", "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" + "Microsoft.Extensions.Primitives": "10.0.2" }, "compile": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { + "lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { "related": ".xml" } }, "runtime": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { + "lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { "related": ".xml" } }, "build": { - "buildTransitive/net6.0/_._": {} + "buildTransitive/net8.0/_._": {} } }, - "Microsoft.Extensions.FileProviders.Physical/8.0.0": { + "Microsoft.Extensions.FileProviders.Physical/10.0.2": { "type": "package", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" }, "compile": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Physical.dll": { + "lib/net10.0/Microsoft.Extensions.FileProviders.Physical.dll": { "related": ".xml" } }, "runtime": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Physical.dll": { + "lib/net10.0/Microsoft.Extensions.FileProviders.Physical.dll": { "related": ".xml" } }, "build": { - "buildTransitive/net6.0/_._": {} + "buildTransitive/net8.0/_._": {} } }, - "Microsoft.Extensions.FileSystemGlobbing/8.0.0": { + "Microsoft.Extensions.FileSystemGlobbing/10.0.2": { "type": "package", "compile": { - "lib/net8.0/Microsoft.Extensions.FileSystemGlobbing.dll": { + "lib/net10.0/Microsoft.Extensions.FileSystemGlobbing.dll": { "related": ".xml" } }, "runtime": { - "lib/net8.0/Microsoft.Extensions.FileSystemGlobbing.dll": { + "lib/net10.0/Microsoft.Extensions.FileSystemGlobbing.dll": { "related": ".xml" } }, "build": { - "buildTransitive/net6.0/_._": {} + "buildTransitive/net8.0/_._": {} } }, "Microsoft.Extensions.Logging/10.0.2": { @@ -443,8 +443,8 @@ "framework": ".NETCoreApp,Version=v10.0", "dependencies": { "Autofac": "9.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", - "Microsoft.Extensions.Configuration.Json": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", + "Microsoft.Extensions.Configuration.Json": "10.0.2", "eSuite.Core": "1.0.0" }, "compile": { @@ -719,64 +719,62 @@ "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Configuration.FileExtensions/8.0.0": { - "sha512": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "Microsoft.Extensions.Configuration.FileExtensions/10.0.2": { + "sha512": "6vStNVa/7hcT6VrYvVMGCWUl/QIKwNeQaSGnKw1E4RPpZbQbOjDsATCbrQUa0sFUs7LW8T9aZ2NBKttMz1+WuA==", "type": "package", - "path": "microsoft.extensions.configuration.fileextensions/8.0.0", + "path": "microsoft.extensions.configuration.fileextensions/10.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Configuration.FileExtensions.targets", "buildTransitive/net462/_._", - "buildTransitive/net6.0/_._", + "buildTransitive/net8.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Configuration.FileExtensions.targets", + "lib/net10.0/Microsoft.Extensions.Configuration.FileExtensions.dll", + "lib/net10.0/Microsoft.Extensions.Configuration.FileExtensions.xml", "lib/net462/Microsoft.Extensions.Configuration.FileExtensions.dll", "lib/net462/Microsoft.Extensions.Configuration.FileExtensions.xml", - "lib/net6.0/Microsoft.Extensions.Configuration.FileExtensions.dll", - "lib/net6.0/Microsoft.Extensions.Configuration.FileExtensions.xml", - "lib/net7.0/Microsoft.Extensions.Configuration.FileExtensions.dll", - "lib/net7.0/Microsoft.Extensions.Configuration.FileExtensions.xml", "lib/net8.0/Microsoft.Extensions.Configuration.FileExtensions.dll", "lib/net8.0/Microsoft.Extensions.Configuration.FileExtensions.xml", + "lib/net9.0/Microsoft.Extensions.Configuration.FileExtensions.dll", + "lib/net9.0/Microsoft.Extensions.Configuration.FileExtensions.xml", "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll", "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.xml", - "microsoft.extensions.configuration.fileextensions.8.0.0.nupkg.sha512", + "microsoft.extensions.configuration.fileextensions.10.0.2.nupkg.sha512", "microsoft.extensions.configuration.fileextensions.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.Configuration.Json/8.0.0": { - "sha512": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "Microsoft.Extensions.Configuration.Json/10.0.2": { + "sha512": "ovjOVr+rNxOT249iezwihlPNMaIJdBC6PMGeMnzhkm5EoKJWFjp3mmvtndfHY6A88X4wulXlidMhmjX8v6V/aw==", "type": "package", - "path": "microsoft.extensions.configuration.json/8.0.0", + "path": "microsoft.extensions.configuration.json/10.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.Configuration.Json.targets", "buildTransitive/net462/_._", - "buildTransitive/net6.0/_._", + "buildTransitive/net8.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.Configuration.Json.targets", + "lib/net10.0/Microsoft.Extensions.Configuration.Json.dll", + "lib/net10.0/Microsoft.Extensions.Configuration.Json.xml", "lib/net462/Microsoft.Extensions.Configuration.Json.dll", "lib/net462/Microsoft.Extensions.Configuration.Json.xml", - "lib/net6.0/Microsoft.Extensions.Configuration.Json.dll", - "lib/net6.0/Microsoft.Extensions.Configuration.Json.xml", - "lib/net7.0/Microsoft.Extensions.Configuration.Json.dll", - "lib/net7.0/Microsoft.Extensions.Configuration.Json.xml", "lib/net8.0/Microsoft.Extensions.Configuration.Json.dll", "lib/net8.0/Microsoft.Extensions.Configuration.Json.xml", + "lib/net9.0/Microsoft.Extensions.Configuration.Json.dll", + "lib/net9.0/Microsoft.Extensions.Configuration.Json.xml", "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll", "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.xml", "lib/netstandard2.1/Microsoft.Extensions.Configuration.Json.dll", "lib/netstandard2.1/Microsoft.Extensions.Configuration.Json.xml", - "microsoft.extensions.configuration.json.8.0.0.nupkg.sha512", + "microsoft.extensions.configuration.json.10.0.2.nupkg.sha512", "microsoft.extensions.configuration.json.nuspec", "useSharedDesignerContext.txt" ] @@ -843,90 +841,89 @@ "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { - "sha512": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "Microsoft.Extensions.FileProviders.Abstractions/10.0.2": { + "sha512": "+r/eJ+slW/EmwWmH3En4gzRg1k6+yTqexoHBrMuz5fxsIKJA8MDiSGepjw/ko3XyNqg+w3dxQe+huoVXs5XDJw==", "type": "package", - "path": "microsoft.extensions.fileproviders.abstractions/8.0.0", + "path": "microsoft.extensions.fileproviders.abstractions/10.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.FileProviders.Abstractions.targets", "buildTransitive/net462/_._", - "buildTransitive/net6.0/_._", + "buildTransitive/net8.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.FileProviders.Abstractions.targets", + "lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll", + "lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.xml", "lib/net462/Microsoft.Extensions.FileProviders.Abstractions.dll", "lib/net462/Microsoft.Extensions.FileProviders.Abstractions.xml", - "lib/net6.0/Microsoft.Extensions.FileProviders.Abstractions.dll", - "lib/net6.0/Microsoft.Extensions.FileProviders.Abstractions.xml", "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll", "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.xml", + "lib/net9.0/Microsoft.Extensions.FileProviders.Abstractions.dll", + "lib/net9.0/Microsoft.Extensions.FileProviders.Abstractions.xml", "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll", "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.xml", - "microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512", + "microsoft.extensions.fileproviders.abstractions.10.0.2.nupkg.sha512", "microsoft.extensions.fileproviders.abstractions.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.FileProviders.Physical/8.0.0": { - "sha512": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "Microsoft.Extensions.FileProviders.Physical/10.0.2": { + "sha512": "4+ypApaugtHIz5Q2Z3oC4+erDbOgy0HrMFYS3Nm3qmTXyqK7sU7LJWY9gci99Wcx6j7ivgk8kdCkgmvsA4t0Ow==", "type": "package", - "path": "microsoft.extensions.fileproviders.physical/8.0.0", + "path": "microsoft.extensions.fileproviders.physical/10.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.FileProviders.Physical.targets", "buildTransitive/net462/_._", - "buildTransitive/net6.0/_._", + "buildTransitive/net8.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.FileProviders.Physical.targets", + "lib/net10.0/Microsoft.Extensions.FileProviders.Physical.dll", + "lib/net10.0/Microsoft.Extensions.FileProviders.Physical.xml", "lib/net462/Microsoft.Extensions.FileProviders.Physical.dll", "lib/net462/Microsoft.Extensions.FileProviders.Physical.xml", - "lib/net6.0/Microsoft.Extensions.FileProviders.Physical.dll", - "lib/net6.0/Microsoft.Extensions.FileProviders.Physical.xml", - "lib/net7.0/Microsoft.Extensions.FileProviders.Physical.dll", - "lib/net7.0/Microsoft.Extensions.FileProviders.Physical.xml", "lib/net8.0/Microsoft.Extensions.FileProviders.Physical.dll", "lib/net8.0/Microsoft.Extensions.FileProviders.Physical.xml", + "lib/net9.0/Microsoft.Extensions.FileProviders.Physical.dll", + "lib/net9.0/Microsoft.Extensions.FileProviders.Physical.xml", "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll", "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.xml", - "microsoft.extensions.fileproviders.physical.8.0.0.nupkg.sha512", + "microsoft.extensions.fileproviders.physical.10.0.2.nupkg.sha512", "microsoft.extensions.fileproviders.physical.nuspec", "useSharedDesignerContext.txt" ] }, - "Microsoft.Extensions.FileSystemGlobbing/8.0.0": { - "sha512": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==", + "Microsoft.Extensions.FileSystemGlobbing/10.0.2": { + "sha512": "XozoMaWcFIv1tv0LDF+YMeZYjiNiNIewpNdZ3TEoVGf8ROrp0hVoEdUyUBsI8oYGM5U3Z5hiNEv0j2Z5COnMgg==", "type": "package", - "path": "microsoft.extensions.filesystemglobbing/8.0.0", + "path": "microsoft.extensions.filesystemglobbing/10.0.2", "files": [ ".nupkg.metadata", ".signature.p7s", "Icon.png", - "LICENSE.TXT", "PACKAGE.md", "THIRD-PARTY-NOTICES.TXT", "buildTransitive/net461/Microsoft.Extensions.FileSystemGlobbing.targets", "buildTransitive/net462/_._", - "buildTransitive/net6.0/_._", + "buildTransitive/net8.0/_._", "buildTransitive/netcoreapp2.0/Microsoft.Extensions.FileSystemGlobbing.targets", + "lib/net10.0/Microsoft.Extensions.FileSystemGlobbing.dll", + "lib/net10.0/Microsoft.Extensions.FileSystemGlobbing.xml", "lib/net462/Microsoft.Extensions.FileSystemGlobbing.dll", "lib/net462/Microsoft.Extensions.FileSystemGlobbing.xml", - "lib/net6.0/Microsoft.Extensions.FileSystemGlobbing.dll", - "lib/net6.0/Microsoft.Extensions.FileSystemGlobbing.xml", - "lib/net7.0/Microsoft.Extensions.FileSystemGlobbing.dll", - "lib/net7.0/Microsoft.Extensions.FileSystemGlobbing.xml", "lib/net8.0/Microsoft.Extensions.FileSystemGlobbing.dll", "lib/net8.0/Microsoft.Extensions.FileSystemGlobbing.xml", + "lib/net9.0/Microsoft.Extensions.FileSystemGlobbing.dll", + "lib/net9.0/Microsoft.Extensions.FileSystemGlobbing.xml", "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll", "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.xml", - "microsoft.extensions.filesystemglobbing.8.0.0.nupkg.sha512", + "microsoft.extensions.filesystemglobbing.10.0.2.nupkg.sha512", "microsoft.extensions.filesystemglobbing.nuspec", "useSharedDesignerContext.txt" ] diff --git a/e-suite.Scheduler/e-suite.Scheduler/DependencyInjection/CoreRegistrationModule.cs b/e-suite.Scheduler/e-suite.Scheduler/DependencyInjection/CoreRegistrationModule.cs index 1d80f5c..71aaa34 100644 --- a/e-suite.Scheduler/e-suite.Scheduler/DependencyInjection/CoreRegistrationModule.cs +++ b/e-suite.Scheduler/e-suite.Scheduler/DependencyInjection/CoreRegistrationModule.cs @@ -36,8 +36,6 @@ public class CoreRegistrationModule : ESuiteModule .As() .InstancePerLifetimeScope(); - //Messaging.Common.IocRegistration.RegisterTypes(builder); - builder.RegisterType(); builder.RegisterType(); } diff --git a/e-suite.Scheduler/e-suite.Scheduler/appsettings.json b/e-suite.Scheduler/e-suite.Scheduler/appsettings.json index a7df20a..bd52181 100644 --- a/e-suite.Scheduler/e-suite.Scheduler/appsettings.json +++ b/e-suite.Scheduler/e-suite.Scheduler/appsettings.json @@ -1,10 +1,5 @@ { "RabbitMQ": { "hostname": "localhost" - }, - "Modules": [ - "e-suite.API.Common.dll", - "e-suite.Database.SqlServer.dll", - "e-suite.Messaging.Common.dll" - ] + } } \ No newline at end of file diff --git a/e-suite.Service.Mail/e-suite.Service.Mail/MailService.cs b/e-suite.Service.Mail/e-suite.Service.Mail/MailService.cs index a379ec2..d690519 100644 --- a/e-suite.Service.Mail/e-suite.Service.Mail/MailService.cs +++ b/e-suite.Service.Mail/e-suite.Service.Mail/MailService.cs @@ -3,6 +3,8 @@ using System.Net.Mail; using System.Net.Mime; using e_suite.API.Common.exceptions; using e_suite.API.Common.repository; +using e_suite.Database.Audit; +using e_suite.Database.Audit.AuditEngine; using e_suite.Service.Mail.Facade; using e_suite.Service.Mail.Helper; using eSuite.Core.MailService; @@ -78,7 +80,31 @@ public class MailService : IMailService try { await client.SendMailAsync(message, cancellationToken); - //todo add audit record here to say that email of mailtype has been sent to user. + var auditUserDetails = new AuditUserDetails + { + UserDisplayName = "Mail service" + }; + + var fields = new Dictionary + { + ["Address"] = new() + { + NewValue = address.Email, + }, + ["DisplayName"] = new() + { + NewValue = address.DisplayName + }, + ["EmailType"] = new() + { + NewValue = emailRequest.EmailType + }, + ["Subject"] = new() + { + NewValue = message.Subject + } + }; + await _userManagerRepository.AddAdhocAuditEntry(auditUserDetails, AuditType.EmailSent, fields, cancellationToken); } catch (Exception e) { diff --git a/eSuite.sln b/eSuite.sln index b1d9b16..39cf24a 100644 --- a/eSuite.sln +++ b/eSuite.sln @@ -159,6 +159,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "e-suite.Service.SigmaImport EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "e-suite.DependencyInjection", "e-suite.DependencyInjection\e-suite.DependencyInjection.csproj", "{1D974411-767E-4242-96F7-E545D285B356}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "e-suite.DependencyInjection.UnitTests", "e-suite.DependencyInjection.UnitTests\e-suite.DependencyInjection.UnitTests.csproj", "{8D343418-7E3A-40E5-A5CF-6497221F3F7E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -445,6 +447,10 @@ Global {1D974411-767E-4242-96F7-E545D285B356}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D974411-767E-4242-96F7-E545D285B356}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D974411-767E-4242-96F7-E545D285B356}.Release|Any CPU.Build.0 = Release|Any CPU + {8D343418-7E3A-40E5-A5CF-6497221F3F7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D343418-7E3A-40E5-A5CF-6497221F3F7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D343418-7E3A-40E5-A5CF-6497221F3F7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D343418-7E3A-40E5-A5CF-6497221F3F7E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -516,6 +522,7 @@ Global {63981A57-8140-CE4B-DFFC-00C31058CDB2} = {27EA902C-3CD0-4A8F-BA75-8D1AF87902AC} {068C2F1E-9DF0-34DC-159D-E75FB31849E4} = {726E46E8-E6E2-44D1-AB3B-85481330A84E} {1D974411-767E-4242-96F7-E545D285B356} = {A0787221-622B-4D00-A566-BF7297378322} + {8D343418-7E3A-40E5-A5CF-6497221F3F7E} = {27EA902C-3CD0-4A8F-BA75-8D1AF87902AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C5175258-F776-4FF9-A9EE-386312E47061}