diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index c353458ebe..e0e28c4942 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -30,6 +30,12 @@ + + + + + + @@ -92,6 +98,8 @@ + + diff --git a/src/ProjectReferences.Persisters.Primary.props b/src/ProjectReferences.Persisters.Primary.props index 255b45ed5c..de0fbcd871 100644 --- a/src/ProjectReferences.Persisters.Primary.props +++ b/src/ProjectReferences.Persisters.Primary.props @@ -1,6 +1,8 @@ + + diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig b/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistence.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistence.cs new file mode 100644 index 0000000000..0f3f01ad9a --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistence.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSql; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using ServiceControl.Persistence.EFCore.Abstractions; + +class PostgreSqlPersistence(PostgreSqlPersisterSettings settings) : BasePersistence, IPersistence +{ + public void AddPersistence(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + RegisterDataStores(services); + } + + public void AddInstaller(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + } + + void RegisterSettings(IServiceCollection services) + { + services.AddSingleton(settings); + services.AddSingleton(settings); + services.AddSingleton(settings); + } + + void ConfigureDbContext(IServiceCollection services) => + services.AddPooledDbContextFactory(options => + options.UseNpgsql(settings.ConnectionString, npgsql => npgsql.CommandTimeout(settings.CommandTimeout))); +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs new file mode 100644 index 0000000000..d6e8705dd3 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersistenceConfiguration.cs @@ -0,0 +1,20 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSql; + +using Microsoft.Extensions.Logging; +using ServiceControl.Infrastructure; +using ServiceControl.Persistence.EFCore.Abstractions; + +class PostgreSqlPersistenceConfiguration : EFPersistenceConfigurationBase +{ + public override IPersistence Create(PersistenceSettings settings) + { + // Temporary until the persister is fully implemented + LoggerUtil.CreateStaticLogger() + .LogError("The PostgreSQL persistence is still under development and is not ready for use"); + + return new PostgreSqlPersistence((PostgreSqlPersisterSettings)settings); + } + + protected override EFPersisterSettings CreateSettings(string connectionString) => + new PostgreSqlPersisterSettings { ConnectionString = connectionString }; +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersisterSettings.cs new file mode 100644 index 0000000000..88cba1ef04 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlPersisterSettings.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSql; + +using ServiceControl.Persistence.EFCore.Abstractions; + +public class PostgreSqlPersisterSettings : EFPersisterSettings +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs new file mode 100644 index 0000000000..3f7baa435e --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs @@ -0,0 +1,8 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSql; + +using Microsoft.EntityFrameworkCore; +using ServiceControl.Persistence.EFCore.DbContexts; + +public class PostgreSqlServiceControlDbContext(DbContextOptions options) : ServiceControlDbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContextFactory.cs new file mode 100644 index 0000000000..5a17acb93f --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContextFactory.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.PostgreSql; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +/// +/// Design-time factory used by the dotnet-ef tooling (e.g. dotnet ef migrations add). +/// +public class PostgreSqlServiceControlDbContextFactory : IDesignTimeDbContextFactory +{ + public PostgreSqlServiceControlDbContext CreateDbContext(string[] args) + { + var connectionString = Environment.GetEnvironmentVariable("SERVICECONTROL_DATABASE_CONNECTIONSTRING") + ?? "Host=localhost;Port=5432;Database=servicecontrol;Username=postgres;Password=postgres"; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseNpgsql(connectionString); + + return new PostgreSqlServiceControlDbContext(optionsBuilder.Options); + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj b/src/ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj new file mode 100644 index 0000000000..f638c069d0 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/ServiceControl.Persistence.EFCore.PostgreSql.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/persistence.manifest b/src/ServiceControl.Persistence.EFCore.PostgreSql/persistence.manifest new file mode 100644 index 0000000000..925712ab78 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/persistence.manifest @@ -0,0 +1,25 @@ +{ + "Name": "PostgreSQL", + "DisplayName": "PostgreSQL", + "Description": "PostgreSQL ServiceControl persister", + "AssemblyName": "ServiceControl.Persistence.EFCore.PostgreSql", + "TypeName": "ServiceControl.Persistence.EFCore.PostgreSql.PostgreSqlPersistenceConfiguration, ServiceControl.Persistence.EFCore.PostgreSql", + "Settings": [ + { + "Name": "ServiceControl/Database/ConnectionString", + "Mandatory": true + }, + { + "Name": "ServiceControl/Database/CommandTimeout", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/StoragePath", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/MinCompressionSize", + "Mandatory": false + } + ] +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig b/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj new file mode 100644 index 0000000000..862beed816 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/ServiceControl.Persistence.EFCore.SqlServer.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs new file mode 100644 index 0000000000..afbf49c124 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistence.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using ServiceControl.Persistence.EFCore.Abstractions; + +class SqlServerPersistence(SqlServerPersisterSettings settings) : BasePersistence, IPersistence +{ + public void AddPersistence(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + RegisterDataStores(services); + } + + public void AddInstaller(IServiceCollection services) + { + RegisterSettings(services); + ConfigureDbContext(services); + } + + void RegisterSettings(IServiceCollection services) + { + services.AddSingleton(settings); + services.AddSingleton(settings); + services.AddSingleton(settings); + } + + void ConfigureDbContext(IServiceCollection services) => + services.AddPooledDbContextFactory(options => + options.UseSqlServer(settings.ConnectionString, sqlServer => sqlServer.CommandTimeout(settings.CommandTimeout))); +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs new file mode 100644 index 0000000000..49d5d8e415 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersistenceConfiguration.cs @@ -0,0 +1,20 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.Extensions.Logging; +using ServiceControl.Infrastructure; +using ServiceControl.Persistence.EFCore.Abstractions; + +class SqlServerPersistenceConfiguration : EFPersistenceConfigurationBase +{ + public override IPersistence Create(PersistenceSettings settings) + { + // Temporary until the persister is fully implemented + LoggerUtil.CreateStaticLogger() + .LogError("The SQL Server persistence is still under development and is not ready for use"); + + return new SqlServerPersistence((SqlServerPersisterSettings)settings); + } + + protected override EFPersisterSettings CreateSettings(string connectionString) => + new SqlServerPersisterSettings { ConnectionString = connectionString }; +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs new file mode 100644 index 0000000000..9f3bd871b4 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerPersisterSettings.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using ServiceControl.Persistence.EFCore.Abstractions; + +public class SqlServerPersisterSettings : EFPersisterSettings +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs new file mode 100644 index 0000000000..528ab19b52 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs @@ -0,0 +1,8 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using ServiceControl.Persistence.EFCore.DbContexts; + +public class SqlServerServiceControlDbContext(DbContextOptions options) : ServiceControlDbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs new file mode 100644 index 0000000000..2be9b44199 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContextFactory.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.SqlServer; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +/// +/// Design-time factory used by the dotnet-ef tooling (e.g. dotnet ef migrations add). +/// +public class SqlServerServiceControlDbContextFactory : IDesignTimeDbContextFactory +{ + public SqlServerServiceControlDbContext CreateDbContext(string[] args) + { + var connectionString = Environment.GetEnvironmentVariable("SERVICECONTROL_DATABASE_CONNECTIONSTRING") + ?? "Server=localhost;Database=ServiceControl;Trusted_Connection=True;TrustServerCertificate=True"; + + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlServer(connectionString); + + return new SqlServerServiceControlDbContext(optionsBuilder.Options); + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest new file mode 100644 index 0000000000..aa1b57b88f --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/persistence.manifest @@ -0,0 +1,25 @@ +{ + "Name": "SQLServer", + "DisplayName": "SQL Server", + "Description": "SQL Server ServiceControl persister", + "AssemblyName": "ServiceControl.Persistence.EFCore.SqlServer", + "TypeName": "ServiceControl.Persistence.EFCore.SqlServer.SqlServerPersistenceConfiguration, ServiceControl.Persistence.EFCore.SqlServer", + "Settings": [ + { + "Name": "ServiceControl/Database/ConnectionString", + "Mandatory": true + }, + { + "Name": "ServiceControl/Database/CommandTimeout", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/StoragePath", + "Mandatory": false + }, + { + "Name": "ServiceControl/MessageBody/MinCompressionSize", + "Mandatory": false + } + ] +} diff --git a/src/ServiceControl.Persistence.EFCore/.editorconfig b/src/ServiceControl.Persistence.EFCore/.editorconfig new file mode 100644 index 0000000000..1a80b3d27b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/.editorconfig @@ -0,0 +1,9 @@ +[*.cs] + +# Justification: ServiceControl app has no synchronization context +dotnet_diagnostic.CA2007.severity = none + +# Disable style rules for auto-generated EF migrations +[Migrations/**.cs] +dotnet_diagnostic.IDE0065.severity = none +generated_code = true \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs new file mode 100644 index 0000000000..0076c9ae81 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/BasePersistence.cs @@ -0,0 +1,50 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +using Microsoft.Extensions.DependencyInjection; +using NServiceBus.Unicast.Subscriptions.MessageDrivenSubscriptions; +using Particular.LicensingComponent.Persistence; +using ServiceControl.Operations.BodyStorage; +using ServiceControl.Persistence.EFCore.Implementation; +using ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; +using ServiceControl.Persistence.MessageRedirects; +using ServiceControl.Persistence.Recoverability; +using ServiceControl.Persistence.UnitOfWork; + +public abstract class BasePersistence +{ + protected static void RegisterDataStores(IServiceCollection services) + { + services.AddSingleton(TimeProvider.System); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + + services.AddUnitOfWorkFactory(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + services.AddHostedService(p => p.GetRequiredService()); + + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); + services.AddHostedService(p => p.GetRequiredService()); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs new file mode 100644 index 0000000000..54f272d30c --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersistenceConfigurationBase.cs @@ -0,0 +1,40 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +using ServiceControl.Configuration; + +public abstract class EFPersistenceConfigurationBase : IPersistenceConfiguration +{ + const string ConnectionStringKey = "Database/ConnectionString"; + const string CommandTimeoutKey = "Database/CommandTimeout"; + const string MessageBodyStoragePathKey = "MessageBody/StoragePath"; + const string MinBodySizeForCompressionKey = "MessageBody/MinCompressionSize"; + const string ErrorRetentionPeriodKey = "ErrorRetentionPeriod"; + const string EnableFullTextSearchOnBodiesKey = "EnableFullTextSearchOnBodies"; + + public PersistenceSettings CreateSettings(SettingsRootNamespace settingsRootNamespace) + { + var settings = CreateSettings(GetRequiredSetting(settingsRootNamespace, ConnectionStringKey)); + + settings.CommandTimeout = SettingsReader.Read(settingsRootNamespace, CommandTimeoutKey, 30); + settings.MessageBodyStoragePath = SettingsReader.Read(settingsRootNamespace, MessageBodyStoragePathKey); + settings.MinBodySizeForCompression = SettingsReader.Read(settingsRootNamespace, MinBodySizeForCompressionKey, 4096); + settings.ErrorRetentionPeriod = GetRequiredSetting(settingsRootNamespace, ErrorRetentionPeriodKey); + settings.EnableFullTextSearchOnBodies = SettingsReader.Read(settingsRootNamespace, EnableFullTextSearchOnBodiesKey, true); + + return settings; + } + + public abstract IPersistence Create(PersistenceSettings settings); + + protected abstract EFPersisterSettings CreateSettings(string connectionString); + + static T GetRequiredSetting(SettingsRootNamespace settingsRootNamespace, string key) + { + if (SettingsReader.TryRead(settingsRootNamespace, key, out var value)) + { + return value; + } + + throw new Exception($"Setting {key} of type {typeof(T)} is required"); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs new file mode 100644 index 0000000000..6789116fb7 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Abstractions/EFPersisterSettings.cs @@ -0,0 +1,10 @@ +namespace ServiceControl.Persistence.EFCore.Abstractions; + +public abstract class EFPersisterSettings : PersistenceSettings +{ + public required string ConnectionString { get; set; } + public int CommandTimeout { get; set; } = 30; + public TimeSpan ErrorRetentionPeriod { get; set; } + public string? MessageBodyStoragePath { get; set; } + public int MinBodySizeForCompression { get; set; } = 4096; +} diff --git a/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs new file mode 100644 index 0000000000..83f28e3310 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/DbContexts/ServiceControlDbContext.cs @@ -0,0 +1,7 @@ +namespace ServiceControl.Persistence.EFCore.DbContexts; + +using Microsoft.EntityFrameworkCore; + +public abstract class ServiceControlDbContext(DbContextOptions options) : DbContext(options) +{ +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs new file mode 100644 index 0000000000..e1caa893a7 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/BodyStorage.cs @@ -0,0 +1,9 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations.BodyStorage; + +public class BodyStorage : IBodyStorage +{ + public Task TryFetch(string bodyId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs new file mode 100644 index 0000000000..9a16177f3d --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/CustomCheckDataStore.cs @@ -0,0 +1,19 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Contracts.CustomChecks; +using ServiceControl.Persistence.Infrastructure; + +public class CustomCheckDataStore : ICustomChecksDataStore +{ + public Task UpdateCustomCheckStatus(CustomCheckDetail detail) => + throw new NotImplementedException(); + + public Task>> GetStats(PagingInfo paging, string? status = null) => + throw new NotImplementedException(); + + public Task DeleteCustomCheck(Guid id) => + throw new NotImplementedException(); + + public Task GetNumberOfFailedChecks() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs new file mode 100644 index 0000000000..aa936dd6dc --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EditFailedMessagesManager.cs @@ -0,0 +1,27 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; + +public class EditFailedMessagesManager : IEditFailedMessagesManager +{ + public Task GetFailedMessage(string failedMessageId) => + throw new NotImplementedException(); + + public Task GetCurrentEditingRequestId(string failedMessageId) => + throw new NotImplementedException(); + + public Task SetCurrentEditingRequestId(string editingMessageId) => + throw new NotImplementedException(); + + public Task SetFailedMessageAsResolved() => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs new file mode 100644 index 0000000000..7e79541e91 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EndpointSettingsStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +public class EndpointSettingsStore : IEndpointSettingsStore +{ + public IAsyncEnumerable GetAllEndpointSettings() => + throw new NotImplementedException(); + + public Task UpdateEndpointSettings(EndpointSettings settings, CancellationToken token) => + throw new NotImplementedException(); + + public Task Delete(string name, CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs new file mode 100644 index 0000000000..2757c59658 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/ErrorMessagesDataStore.cs @@ -0,0 +1,111 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.CompositeViews.Messages; +using ServiceControl.EventLog; +using ServiceControl.MessageFailures; +using ServiceControl.MessageFailures.Api; +using ServiceControl.Operations; +using ServiceControl.Persistence.Infrastructure; +using ServiceControl.Recoverability; + +public class ErrorMessagesDataStore : IErrorMessageDataStore +{ + public Task>> GetAllMessages(PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesForEndpoint(string endpointName, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesByConversation(string conversationId, PagingInfo pagingInfo, SortInfo sortInfo, bool includeSystemMessages) => + throw new NotImplementedException(); + + public Task>> GetAllMessagesForSearch(string searchTerms, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task>> SearchEndpointMessages(string endpointName, string searchKeyword, PagingInfo pagingInfo, SortInfo sortInfo, DateTimeRange? timeSentRange = null) => + throw new NotImplementedException(); + + public Task FailedMessageMarkAsArchived(string failedMessageId) => + throw new NotImplementedException(); + + public Task FailedMessagesFetch(Guid[] ids) => + throw new NotImplementedException(); + + public Task StoreFailedErrorImport(FailedErrorImport failure) => + throw new NotImplementedException(); + + public Task CreateEditFailedMessageManager() => + throw new NotImplementedException(); + + public Task> GetFailureGroupView(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task> GetFailureGroupsByClassifier(string classifier) => + throw new NotImplementedException(); + + public Task>> ErrorGet(string status, string modified, string queueAddress, PagingInfo pagingInfo, SortInfo sortInfo) => + throw new NotImplementedException(); + + public Task ErrorsHead(string status, string modified, string queueAddress) => + throw new NotImplementedException(); + + public Task>> ErrorsByEndpointName(string status, string endpointName, string modified, PagingInfo pagingInfo, SortInfo sortInfo) => + throw new NotImplementedException(); + + public Task> ErrorsSummary() => + throw new NotImplementedException(); + + public Task ErrorLastBy(string failedMessageId) => + throw new NotImplementedException(); + + public Task ErrorBy(string failedMessageId) => + throw new NotImplementedException(); + + public Task CreateNotificationsManager() => + throw new NotImplementedException(); + + public Task EditComment(string groupId, string comment) => + throw new NotImplementedException(); + + public Task DeleteComment(string groupId) => + throw new NotImplementedException(); + + public Task>> GetGroupErrors(string groupId, string status, string modified, SortInfo sortInfo, PagingInfo pagingInfo) => + throw new NotImplementedException(); + + public Task GetGroupErrorsCount(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task>> GetGroup(string groupId, string status, string modified) => + throw new NotImplementedException(); + + public Task MarkMessageAsResolved(string failedMessageId) => + throw new NotImplementedException(); + + public Task ProcessPendingRetries(DateTime periodFrom, DateTime periodTo, string queueAddress, Func processCallback) => + throw new NotImplementedException(); + + public Task UnArchiveMessagesByRange(DateTime from, DateTime to) => + throw new NotImplementedException(); + + public Task UnArchiveMessages(IEnumerable failedMessageIds) => + throw new NotImplementedException(); + + public Task RevertRetry(string messageUniqueId) => + throw new NotImplementedException(); + + public Task RemoveFailedMessageRetryDocument(string uniqueMessageId) => + throw new NotImplementedException(); + + public Task GetRetryPendingMessages(DateTime from, DateTime to, string queueAddress) => + throw new NotImplementedException(); + + public Task FetchFromFailedMessage(string uniqueMessageId) => + throw new NotImplementedException(); + + public Task StoreEventLogItem(EventLogItem logItem) => + throw new NotImplementedException(); + + public Task StoreFailedMessagesForTestsOnly(params FailedMessage[] failedMessages) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs new file mode 100644 index 0000000000..1b9799b989 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/EventLogDataStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.EventLog; +using ServiceControl.Persistence.Infrastructure; + +public class EventLogDataStore : IEventLogDataStore +{ + public Task Add(EventLogItem logItem) => + throw new NotImplementedException(); + + public Task<(IList items, long total, string version)> GetEventLogItems(PagingInfo pagingInfo) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs new file mode 100644 index 0000000000..dfa5094631 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/ExternalIntegrationRequestsDataStore.cs @@ -0,0 +1,19 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using Microsoft.Extensions.Hosting; +using ServiceControl.ExternalIntegrations; + +public class ExternalIntegrationRequestsDataStore : IExternalIntegrationRequestsDataStore, IHostedService +{ + public void Subscribe(Func callback) => + throw new NotImplementedException(); + + public Task StoreDispatchRequest(IEnumerable dispatchRequests) => + throw new NotImplementedException(); + + public Task StartAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StopAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs new file mode 100644 index 0000000000..ed2dd126db --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedErrorImportDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations; + +public class FailedErrorImportDataStore : IFailedErrorImportDataStore +{ + public Task ProcessFailedErrorImports(Func processMessage, CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task QueryContainsFailedImports() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs new file mode 100644 index 0000000000..583e6de887 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/FailedMessageViewIndexNotifications.cs @@ -0,0 +1,15 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using Microsoft.Extensions.Hosting; + +public class FailedMessageViewIndexNotifications : IFailedMessageViewIndexNotifications, IHostedService +{ + public IDisposable Subscribe(Func callback) => + throw new NotImplementedException(); + + public Task StartAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StopAsync(CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs new file mode 100644 index 0000000000..41b18e12df --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Recoverability; + +public class GroupsDataStore : IGroupsDataStore +{ + public Task> GetFailureGroupsByClassifier(string classifier, string classifierFilter) => + throw new NotImplementedException(); + + public Task GetCurrentForwardingBatch() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs new file mode 100644 index 0000000000..03a0c1e6fe --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/LicensingDataStore.cs @@ -0,0 +1,21 @@ +using Particular.LicensingComponent.Contracts; +using Particular.LicensingComponent.Persistence; + +class LicensingDataStore : ILicensingDataStore +{ + public Task> GetAllEndpoints(bool includePlatformEndpoints, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task GetAuditServiceMetadata(CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public Task GetBrokerMetadata(CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task GetEndpoint(EndpointIdentifier id, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + public Task> GetEndpoints(IList endpointIds, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task>> GetEndpointThroughputByQueueName(IList queueNames, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task> GetReportMasks(CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task IsThereThroughputForLastXDays(int days, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task IsThereThroughputForLastXDaysForSource(int days, ThroughputSource throughputSource, bool includeToday, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task RecordEndpointThroughput(string endpointName, ThroughputSource throughputSource, IList throughput, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveAuditServiceMetadata(AuditServiceMetadata auditServiceMetadata, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveBrokerMetadata(BrokerMetadata brokerMetadata, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveEndpoint(Particular.LicensingComponent.Contracts.Endpoint endpoint, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task SaveReportMasks(List reportMasks, CancellationToken cancellationToken) => throw new NotImplementedException(); + public Task UpdateUserIndicatorOnEndpoints(List userIndicatorUpdates, CancellationToken cancellationToken) => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs new file mode 100644 index 0000000000..9ec470e855 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MessageArchiver.cs @@ -0,0 +1,32 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Infrastructure.Auth; +using ServiceControl.Persistence.Recoverability; +using ServiceControl.Recoverability; + +public class MessageArchiver : IArchiveMessages +{ + public Task ArchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null) => + throw new NotImplementedException(); + + public Task UnarchiveAllInGroup(string groupId, AuditUser? initiatedBy = null, string? operationId = null) => + throw new NotImplementedException(); + + public bool IsOperationInProgressFor(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public bool IsArchiveInProgressFor(string groupId) => + throw new NotImplementedException(); + + public void DismissArchiveOperation(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public Task StartArchiving(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public Task StartUnarchiving(string groupId, ArchiveType archiveType) => + throw new NotImplementedException(); + + public IEnumerable GetArchivalOperations() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs new file mode 100644 index 0000000000..d13276f822 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MessageRedirectsDataStore.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Persistence.MessageRedirects; + +public class MessageRedirectsDataStore : IMessageRedirectsDataStore +{ + public Task GetOrCreate() => + throw new NotImplementedException(); + + public Task Save(MessageRedirectsCollection redirects) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs new file mode 100644 index 0000000000..d6ce70598f --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/MonitoringDataStore.cs @@ -0,0 +1,24 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Operations; + +public class MonitoringDataStore : IMonitoringDataStore +{ + public Task CreateIfNotExists(EndpointDetails endpoint) => + throw new NotImplementedException(); + + public Task CreateOrUpdate(EndpointDetails endpoint, IEndpointInstanceMonitoring endpointInstanceMonitoring) => + throw new NotImplementedException(); + + public Task UpdateEndpointMonitoring(EndpointDetails endpoint, bool isMonitored) => + throw new NotImplementedException(); + + public Task WarmupMonitoringFromPersistence(IEndpointInstanceMonitoring endpointInstanceMonitoring) => + throw new NotImplementedException(); + + public Task Delete(Guid endpointId) => + throw new NotImplementedException(); + + public Task> GetAllKnownEndpoints() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs new file mode 100644 index 0000000000..74dabd8071 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/NotificationsManager.cs @@ -0,0 +1,18 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Notifications; + +public class NotificationsManager : INotificationsManager +{ + public Task LoadSettings(TimeSpan? cacheTimeout = null) => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs new file mode 100644 index 0000000000..b53673b630 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/QueueAddressStore.cs @@ -0,0 +1,13 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.Infrastructure; + +public class QueueAddressStore : IQueueAddressStore +{ + public Task>> GetAddresses(PagingInfo pagingInfo) => + throw new NotImplementedException(); + + public Task>> GetAddressesBySearchTerm(string search, PagingInfo pagingInfo) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs new file mode 100644 index 0000000000..ded8d774a8 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesDataStore.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Recoverability; + +public class RetryBatchesDataStore : IRetryBatchesDataStore +{ + public Task CreateRetryBatchesManager() => + throw new NotImplementedException(); + + public Task RecordFailedStagingAttempt(IReadOnlyCollection messages, + IReadOnlyDictionary failedMessageRetriesById, Exception e, + int maxStagingAttempts, string stagingId) => + throw new NotImplementedException(); + + public Task IncrementAttemptCounter(FailedMessageRetry failedMessageRetry) => + throw new NotImplementedException(); + + public Task DeleteFailedMessageRetry(string makeDocumentId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs new file mode 100644 index 0000000000..9348fe0ab8 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryBatchesManager.cs @@ -0,0 +1,50 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.MessageRedirects; +using ServiceControl.Recoverability; + +public class RetryBatchesManager : IRetryBatchesManager +{ + public void Delete(RetryBatch retryBatch) => + throw new NotImplementedException(); + + public void Delete(RetryBatchNowForwarding forwardingBatch) => + throw new NotImplementedException(); + + public Task GetFailedMessageRetries(IList stagingBatchFailureRetries) => + throw new NotImplementedException(); + + public void Evict(FailedMessageRetry failedMessageRetry) => + throw new NotImplementedException(); + + public Task GetFailedMessages(Dictionary.KeyCollection keys) => + throw new NotImplementedException(); + + public Task GetRetryBatchNowForwarding() => + throw new NotImplementedException(); + + public Task GetRetryBatch(string retryBatchId, CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task GetStagingBatch() => + throw new NotImplementedException(); + + public Task Store(RetryBatchNowForwarding retryBatchNowForwarding) => + throw new NotImplementedException(); + + public Task GetOrCreateMessageRedirectsCollection() => + throw new NotImplementedException(); + + public Task CancelExpiration(FailedMessage failedMessage) => + throw new NotImplementedException(); + + public Task SaveChanges() => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs new file mode 100644 index 0000000000..f29758b8a5 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryDocumentDataStore.cs @@ -0,0 +1,41 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.Infrastructure; +using ServiceControl.Recoverability; + +public class RetryDocumentDataStore : IRetryDocumentDataStore +{ + public Task StageRetryByUniqueMessageIds(string batchDocumentId, string[] messageIds) => + throw new NotImplementedException(); + + public Task MoveBatchToStaging(string batchDocumentId) => + throw new NotImplementedException(); + + public Task CreateBatchDocument(string retrySessionId, string requestId, RetryType retryType, + string[] failedMessageRetryIds, string originator, DateTime startTime, DateTime? last = null, + string? batchName = null, string? classifier = null, + string? initiatedById = null, string? initiatedByName = null, string? operationId = null) => + throw new NotImplementedException(); + + public Task>> QueryOrphanedBatches(string retrySessionId) => + throw new NotImplementedException(); + + public Task> QueryAvailableBatches() => + throw new NotImplementedException(); + + public Task GetBatchesForAll(DateTime cutoff, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForEndpoint(DateTime cutoff, string endpoint, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForFailedQueueAddress(DateTime cutoff, string failedQueueAddresspoint, FailedMessageStatus status, Func callback) => + throw new NotImplementedException(); + + public Task GetBatchesForFailureGroup(string groupId, string groupTitle, string groupType, DateTime cutoff, Func callback) => + throw new NotImplementedException(); + + public Task QueryFailureGroupViewOnGroupId(string groupId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs new file mode 100644 index 0000000000..bd514be05b --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/RetryHistoryDataStore.cs @@ -0,0 +1,16 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using ServiceControl.Recoverability; + +public class RetryHistoryDataStore : IRetryHistoryDataStore +{ + public Task GetRetryHistory() => + throw new NotImplementedException(); + + public Task RecordRetryOperationCompleted(string requestId, RetryType retryType, DateTime startTime, DateTime completionTime, + string originator, string classifier, bool messageFailed, int numberOfMessagesProcessed, DateTime lastProcessed, int retryHistoryDepth) => + throw new NotImplementedException(); + + public Task AcknowledgeRetryGroup(string groupId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs b/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs new file mode 100644 index 0000000000..6b4d8c6e93 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/SubscriptionStorage.cs @@ -0,0 +1,20 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +using NServiceBus.Extensibility; +using NServiceBus.Unicast.Subscriptions; +using NServiceBus.Unicast.Subscriptions.MessageDrivenSubscriptions; + +public class SubscriptionStorage : IServiceControlSubscriptionStorage +{ + public Task Initialize() => + throw new NotImplementedException(); + + public Task Subscribe(Subscriber subscriber, MessageType messageType, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public Task Unsubscribe(Subscriber subscriber, MessageType messageType, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); + + public Task> GetSubscriberAddressesForMessage(IEnumerable messageTypes, ContextBag context, CancellationToken cancellationToken = default) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs b/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs new file mode 100644 index 0000000000..e10739ee89 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/TrialLicenseDataProvider.cs @@ -0,0 +1,10 @@ +namespace ServiceControl.Persistence.EFCore.Implementation; + +public class TrialLicenseDataProvider : ITrialLicenseDataProvider +{ + public Task GetTrialEndDate(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public Task StoreTrialEndDate(DateOnly trialEndDate, CancellationToken cancellationToken) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs new file mode 100644 index 0000000000..2e0b2ab1ae --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWork.cs @@ -0,0 +1,21 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFIngestionUnitOfWork : IIngestionUnitOfWork +{ + public IMonitoringIngestionUnitOfWork Monitoring => + throw new NotImplementedException(); + + public IRecoverabilityIngestionUnitOfWork Recoverability => + throw new NotImplementedException(); + + public Task Complete(CancellationToken cancellationToken) => + throw new NotImplementedException(); + + public void Dispose() + { + // Nothing to dispose yet + GC.SuppressFinalize(this); + } +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs new file mode 100644 index 0000000000..2aa8efe4c6 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFIngestionUnitOfWorkFactory.cs @@ -0,0 +1,12 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFIngestionUnitOfWorkFactory : IIngestionUnitOfWorkFactory +{ + public ValueTask StartNew() => + throw new NotImplementedException(); + + public bool CanIngestMore() => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs new file mode 100644 index 0000000000..af7080b6a5 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFMonitoringIngestionUnitOfWork.cs @@ -0,0 +1,9 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using ServiceControl.Persistence.UnitOfWork; + +public class EFMonitoringIngestionUnitOfWork : IMonitoringIngestionUnitOfWork +{ + public Task RecordKnownEndpoint(KnownEndpoint knownEndpoint) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs new file mode 100644 index 0000000000..7e7e93f63e --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs @@ -0,0 +1,16 @@ +namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; + +using NServiceBus.Transport; +using ServiceControl.MessageFailures; +using ServiceControl.Persistence.UnitOfWork; + +public class EFRecoverabilityIngestionUnitOfWork : IRecoverabilityIngestionUnitOfWork +{ + public Task RecordFailedProcessingAttempt(MessageContext context, + FailedMessage.ProcessingAttempt processingAttempt, + List groups) => + throw new NotImplementedException(); + + public Task RecordSuccessfulRetry(string retriedMessageUniqueId) => + throw new NotImplementedException(); +} diff --git a/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj b/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj new file mode 100644 index 0000000000..f80428271a --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore/ServiceControl.Persistence.EFCore.csproj @@ -0,0 +1,27 @@ + + + + net10.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + diff --git a/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs b/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs index 451009ee88..015552e16a 100644 --- a/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs +++ b/src/ServiceControl.Persistence/DevelopmentPersistenceLocations.cs @@ -18,6 +18,8 @@ static DevelopmentPersistenceLocations() if (!string.IsNullOrWhiteSpace(srcFolder) && srcFolder.EndsWith("src")) { ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.RavenDB")); + ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.EFCore.SqlServer")); + ManifestFiles.Add(BuildManifestPath(srcFolder, "ServiceControl.Persistence.EFCore.PostgreSQL")); } } diff --git a/src/ServiceControl.slnx b/src/ServiceControl.slnx index fc2f5dd384..76d72c12f1 100644 --- a/src/ServiceControl.slnx +++ b/src/ServiceControl.slnx @@ -41,6 +41,9 @@ + + + diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt index 59a50c03c0..0489c1d713 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt +++ b/src/ServiceControlInstaller.Engine.UnitTests/ApprovalFiles/PersistenceManifestTests.ApprovePrimaryInstanceManifests.approved.txt @@ -1,4 +1,6 @@ [ + "PostgreSQL: PostgreSQL", "RavenDB: RavenDB", - "RavenDB35: RavenDB 3.5 (Legacy)" + "RavenDB35: RavenDB 3.5 (Legacy)", + "SQLServer: SQL Server" ] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs b/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs index b4d5461092..9460ed9dcf 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs +++ b/src/ServiceControlInstaller.Packaging.UnitTests/PrimaryDeploymentPackageTests.cs @@ -15,8 +15,7 @@ public PrimaryDeploymentPackageTests() public void Should_package_storages_individually() { var expectedPersisters = new[] { - "RavenDB35", // Still must exist, as Raven35 persistence.manifest file must be available for SCMU to understand old versions - "RavenDB" + "RavenDB", "SQLServer", "RavenDB35", "PostgreSQL" }; var persisters = deploymentPackage.DeploymentUnits.Where(u => u.Category == "Persisters");