From cd01014539f9db978e1098fb7bce4cb4dde19d19 Mon Sep 17 00:00:00 2001 From: Mads Larsen Date: Sun, 9 Mar 2025 19:39:45 +0100 Subject: [PATCH] Update ElasticSearch to v1 --- EventFlow.sln | 17 ++++ README.md | 2 +- RELEASE_NOTES.md | 3 +- .../EventFlow.Elasticsearch.Tests.csproj | 8 +- .../ElasticsearchReadModelStoreTests.cs | 19 ++--- .../ElasticsearchThingyMessageReadModel.cs | 79 ++++++++++--------- .../ElasticsearchThingyReadModel.cs | 11 ++- .../EventFlow.Elasticsearch.csproj | 5 +- .../Extensions/EventFlowOptionsExtensions.cs | 27 ++----- .../ReadStores/ElasticsearchReadModelStore.cs | 13 +-- 10 files changed, 100 insertions(+), 84 deletions(-) diff --git a/EventFlow.sln b/EventFlow.sln index 9e06b86fe..5881b6566 100644 --- a/EventFlow.sln +++ b/EventFlow.sln @@ -74,6 +74,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.SourceGenerators" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.SourceGenerators.Tests", "Source\EventFlow.SourceGenerators.Tests\EventFlow.SourceGenerators.Tests.csproj", "{D8317769-F140-4E5D-9041-19342A3A33BD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ElasticSearch", "ElasticSearch", "{B684211A-CF37-4A6A-BF1B-460D0369DD61}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.Elasticsearch", "Source\EventFlow.Elasticsearch\EventFlow.Elasticsearch.csproj", "{199E59E3-C674-4D8E-B31D-699C7331F4CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventFlow.Elasticsearch.Tests", "Source\EventFlow.Elasticsearch.Tests\EventFlow.Elasticsearch.Tests.csproj", "{20775513-2FA6-4148-8068-B7C0AA08EB76}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -172,6 +178,14 @@ Global {D8317769-F140-4E5D-9041-19342A3A33BD}.Debug|Any CPU.Build.0 = Debug|Any CPU {D8317769-F140-4E5D-9041-19342A3A33BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D8317769-F140-4E5D-9041-19342A3A33BD}.Release|Any CPU.Build.0 = Release|Any CPU + {199E59E3-C674-4D8E-B31D-699C7331F4CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {199E59E3-C674-4D8E-B31D-699C7331F4CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {199E59E3-C674-4D8E-B31D-699C7331F4CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {199E59E3-C674-4D8E-B31D-699C7331F4CA}.Release|Any CPU.Build.0 = Release|Any CPU + {20775513-2FA6-4148-8068-B7C0AA08EB76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20775513-2FA6-4148-8068-B7C0AA08EB76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20775513-2FA6-4148-8068-B7C0AA08EB76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20775513-2FA6-4148-8068-B7C0AA08EB76}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -208,6 +222,9 @@ Global {91929C7C-D9AE-B5BA-2467-C9FCAEC69696} = {5EE323DE-E69B-451A-8AC3-22DD6A004FBA} {35098BEA-A0B8-4E63-8D71-35CE5297B459} = {91929C7C-D9AE-B5BA-2467-C9FCAEC69696} {D8317769-F140-4E5D-9041-19342A3A33BD} = {91929C7C-D9AE-B5BA-2467-C9FCAEC69696} + {B684211A-CF37-4A6A-BF1B-460D0369DD61} = {5EE323DE-E69B-451A-8AC3-22DD6A004FBA} + {199E59E3-C674-4D8E-B31D-699C7331F4CA} = {B684211A-CF37-4A6A-BF1B-460D0369DD61} + {20775513-2FA6-4148-8068-B7C0AA08EB76} = {B684211A-CF37-4A6A-BF1B-460D0369DD61} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17607E2C-4E8E-45A2-85BD-0A5808E1C0F3} diff --git a/README.md b/README.md index fa5d8f6a3..0d4237453 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The following list key characteristics of each version as well as its related br - 🟠 `EventFlow.AspNetCore` - 💀 `EventFlow.Autofac` - 💀 `EventFlow.DependencyInjection` - - 🟠 `EventFlow.Elasticsearch` + - 🟢 `EventFlow.Elasticsearch` - 🟢 `EventFlow.EntityFramework` - 🟠 `EventFlow.EventStores.EventStore` - 🟢 `EventFlow.Hangfire` diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 4b9b5204c..7e057a25f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,5 @@ ### New in 1.2.1 (working version, not released yet) - -* *Nothing yet...* +* New: NuGet `EventFlow.Elasticsearch` now ported to v1 (thanks @MMonrad) ### New in 1.2.0 (released 2025-03-09) diff --git a/Source/EventFlow.Elasticsearch.Tests/EventFlow.Elasticsearch.Tests.csproj b/Source/EventFlow.Elasticsearch.Tests/EventFlow.Elasticsearch.Tests.csproj index 2a5dbadd5..71435bbf8 100644 --- a/Source/EventFlow.Elasticsearch.Tests/EventFlow.Elasticsearch.Tests.csproj +++ b/Source/EventFlow.Elasticsearch.Tests/EventFlow.Elasticsearch.Tests.csproj @@ -1,7 +1,6 @@  - - netcoreapp3.1 + net8.0 True False False @@ -11,13 +10,16 @@ - + + + + diff --git a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ElasticsearchReadModelStoreTests.cs b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ElasticsearchReadModelStoreTests.cs index 0c3123bc4..22690f347 100644 --- a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ElasticsearchReadModelStoreTests.cs +++ b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ElasticsearchReadModelStoreTests.cs @@ -37,6 +37,7 @@ using EventFlow.TestHelpers.Aggregates; using EventFlow.TestHelpers.Aggregates.Entities; using EventFlow.TestHelpers.Suites; +using Microsoft.Extensions.DependencyInjection; using Nest; using NUnit.Framework; using IndexName = EventFlow.Elasticsearch.ValueObjects.IndexName; @@ -52,7 +53,7 @@ public class ElasticsearchReadModelStoreTests : TestSuiteForReadModelStore private readonly List _indexes = new List(); - protected override IRootResolver CreateRootResolver(IEventFlowOptions eventFlowOptions) + protected override IServiceProvider Configure(IEventFlowOptions eventFlowOptions) { var elasticsearchUrl = Environment.GetEnvironmentVariable("ELASTICSEARCH_URL") ?? "http://localhost:9200"; @@ -63,25 +64,25 @@ protected override IRootResolver CreateRootResolver(IEventFlowOptions eventFlowO .SniffLifeSpan(TimeSpan.FromMinutes(5)) .DisablePing(); - var resolver = eventFlowOptions - .RegisterServices(sr => { sr.RegisterType(typeof(ThingyMessageLocator)); }) + var options = eventFlowOptions + .RegisterServices(sr => { sr.AddTransient(); }) .ConfigureElasticsearch(connectionSettings) .UseElasticsearchReadModel() .UseElasticsearchReadModel() .AddQueryHandlers( typeof(ElasticsearchThingyGetQueryHandler), typeof(ElasticsearchThingyGetVersionQueryHandler), - typeof(ElasticsearchThingyGetMessagesQueryHandler)) - .CreateResolver(); + typeof(ElasticsearchThingyGetMessagesQueryHandler)); - PrepareIndexes(resolver); + PrepareIndexes(options); - return resolver; + return base.Configure(eventFlowOptions); } - private void PrepareIndexes(IRootResolver resolver) + private void PrepareIndexes(IEventFlowOptions options) { - _elasticClient = resolver.Resolve(); + using var provider = options.ServiceCollection.BuildServiceProvider(); + _elasticClient = provider.GetRequiredService(); var readModelTypes = GetLoadableTypes(typeof(ElasticsearchThingyReadModel).Assembly); diff --git a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyMessageReadModel.cs b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyMessageReadModel.cs index 5eb2a55b3..87aa5f41b 100644 --- a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyMessageReadModel.cs +++ b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyMessageReadModel.cs @@ -21,6 +21,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Linq; +using System.Threading; +using System.Threading.Tasks; using EventFlow.Aggregates; using EventFlow.ReadStores; using EventFlow.TestHelpers.Aggregates; @@ -28,49 +30,54 @@ using EventFlow.TestHelpers.Aggregates.Events; using Nest; -namespace EventFlow.Elasticsearch.Tests.IntegrationTests.ReadModels +namespace EventFlow.Elasticsearch.Tests.IntegrationTests.ReadModels; + +[ElasticsearchType(IdProperty = "Id", RelationName = "message")] +public class ElasticsearchThingyMessageReadModel : IReadModel, + IAmReadModelFor, + IAmReadModelFor { - [ElasticsearchType(IdProperty = "Id", RelationName = "message")] - public class ElasticsearchThingyMessageReadModel : IReadModel, - IAmReadModelFor, - IAmReadModelFor - { - public string Id { get; set; } + public string Id { get; set; } - [Keyword( - Name = "ThingyId", - Index = true)] - public string ThingyId { get; set; } + [Keyword( + Name = "ThingyId", + Index = true)] + public string ThingyId { get; set; } - [Text( - Name = "Message", - Index = false)] - public string Message { get; set; } + [Text( + Name = "Message", + Index = false)] + public string Message { get; set; } - public void Apply(IReadModelContext context, IDomainEvent domainEvent) - { - ThingyId = domainEvent.AggregateIdentity.Value; + public Task ApplyAsync(IReadModelContext context, + IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + ThingyId = domainEvent.AggregateIdentity.Value; - var thingyMessage = domainEvent.AggregateEvent.ThingyMessage; - Id = thingyMessage.Id.Value; - Message = thingyMessage.Message; - } + var thingyMessage = domainEvent.AggregateEvent.ThingyMessage; + Id = thingyMessage.Id.Value; + Message = thingyMessage.Message; + return Task.CompletedTask; + } - public void Apply(IReadModelContext context, IDomainEvent domainEvent) - { - ThingyId = domainEvent.AggregateIdentity.Value; + public Task ApplyAsync(IReadModelContext context, + IDomainEvent domainEvent, + CancellationToken cancellationToken) + { + ThingyId = domainEvent.AggregateIdentity.Value; - var messageId = new ThingyMessageId(context.ReadModelId); - var thingyMessage = domainEvent.AggregateEvent.ThingyMessages.Single(m => m.Id == messageId); - Id = messageId.Value; - Message = thingyMessage.Message; - } + var messageId = new ThingyMessageId(context.ReadModelId); + var thingyMessage = domainEvent.AggregateEvent.ThingyMessages.Single(m => m.Id == messageId); + Id = messageId.Value; + Message = thingyMessage.Message; + return Task.CompletedTask; + } - public ThingyMessage ToThingyMessage() - { - return new ThingyMessage( - ThingyMessageId.With(Id), - Message); - } + public ThingyMessage ToThingyMessage() + { + return new ThingyMessage( + ThingyMessageId.With(Id), + Message); } } \ No newline at end of file diff --git a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyReadModel.cs b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyReadModel.cs index e048e9b43..c90a5671f 100644 --- a/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyReadModel.cs +++ b/Source/EventFlow.Elasticsearch.Tests/IntegrationTests/ReadModels/ElasticsearchThingyReadModel.cs @@ -20,6 +20,8 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +using System.Threading; +using System.Threading.Tasks; using EventFlow.Aggregates; using EventFlow.ReadStores; using EventFlow.TestHelpers.Aggregates; @@ -49,21 +51,24 @@ public class ElasticsearchThingyReadModel : IReadModel, Index = false)] public int PingsReceived { get; set; } - public void Apply(IReadModelContext context, IDomainEvent domainEvent) + public Task ApplyAsync(IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { Id = domainEvent.AggregateIdentity.Value; DomainErrorAfterFirstReceived = true; + return Task.CompletedTask; } - public void Apply(IReadModelContext context, IDomainEvent domainEvent) + public Task ApplyAsync(IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { Id = domainEvent.AggregateIdentity.Value; PingsReceived++; + return Task.CompletedTask; } - public void Apply(IReadModelContext context, IDomainEvent domainEvent) + public Task ApplyAsync(IReadModelContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { context.MarkForDeletion(); + return Task.CompletedTask; } public Thingy ToThingy() diff --git a/Source/EventFlow.Elasticsearch/EventFlow.Elasticsearch.csproj b/Source/EventFlow.Elasticsearch/EventFlow.Elasticsearch.csproj index e95c50047..116baa506 100644 --- a/Source/EventFlow.Elasticsearch/EventFlow.Elasticsearch.csproj +++ b/Source/EventFlow.Elasticsearch/EventFlow.Elasticsearch.csproj @@ -1,7 +1,6 @@  - - netstandard2.0 + netstandard2.1;netcoreapp3.1;net6.0;net8.0 True True False @@ -9,7 +8,7 @@ EventFlow.Elasticsearch Rasmus Mikkelsen Rasmus Mikkelsen - Copyright (c) Rasmus Mikkelsen 2015 - 2021 + Copyright (c) Rasmus Mikkelsen 2015 - 2025 Elasticsearch support for EventFlow CQRS ES event sourcing Elasticsearch git diff --git a/Source/EventFlow.Elasticsearch/Extensions/EventFlowOptionsExtensions.cs b/Source/EventFlow.Elasticsearch/Extensions/EventFlowOptionsExtensions.cs index f8b95a74e..6e2f5f09f 100644 --- a/Source/EventFlow.Elasticsearch/Extensions/EventFlowOptionsExtensions.cs +++ b/Source/EventFlow.Elasticsearch/Extensions/EventFlowOptionsExtensions.cs @@ -23,12 +23,10 @@ using System; using System.Linq; using Elasticsearch.Net; -using EventFlow.Aggregates; -using EventFlow.Configuration; -using EventFlow.Core; using EventFlow.Elasticsearch.ReadStores; using EventFlow.Extensions; using EventFlow.ReadStores; +using Microsoft.Extensions.DependencyInjection; using Nest; namespace EventFlow.Elasticsearch.Extensions @@ -70,8 +68,8 @@ public static IEventFlowOptions ConfigureElasticsearch( { return eventFlowOptions.RegisterServices(sr => { - sr.Register(f => elasticClientFactory(), Lifetime.Singleton); - sr.Register(Lifetime.Singleton, true); + sr.AddSingleton(_ => elasticClientFactory()); + sr.AddSingleton(); }); } @@ -94,24 +92,11 @@ public static IEventFlowOptions UseElasticsearchReadModel, TReadModel, TReadModelLocator>(); } - [Obsolete("Use the simpler method UseElasticsearchReadModel instead.")] - public static IEventFlowOptions UseElasticsearchReadModelFor( - this IEventFlowOptions eventFlowOptions) - where TAggregate : IAggregateRoot - where TIdentity : IIdentity - where TReadModel : class, IReadModel - { - return eventFlowOptions - .RegisterServices(RegisterElasticsearchReadStore) - .UseReadStoreFor, TReadModel>(); - } - - private static void RegisterElasticsearchReadStore( - IServiceRegistration serviceRegistration) + private static void RegisterElasticsearchReadStore(IServiceCollection collection) where TReadModel : class, IReadModel { - serviceRegistration.Register, ElasticsearchReadModelStore>(); - serviceRegistration.Register>(r => r.Resolver.Resolve>()); + collection.AddTransient, ElasticsearchReadModelStore>(); + collection.AddTransient>(r => r.GetRequiredService>()); } } } \ No newline at end of file diff --git a/Source/EventFlow.Elasticsearch/ReadStores/ElasticsearchReadModelStore.cs b/Source/EventFlow.Elasticsearch/ReadStores/ElasticsearchReadModelStore.cs index feedad1a7..8323dba5d 100644 --- a/Source/EventFlow.Elasticsearch/ReadStores/ElasticsearchReadModelStore.cs +++ b/Source/EventFlow.Elasticsearch/ReadStores/ElasticsearchReadModelStore.cs @@ -32,8 +32,8 @@ using EventFlow.Elasticsearch.ValueObjects; using EventFlow.Exceptions; using EventFlow.Extensions; -using EventFlow.Logs; using EventFlow.ReadStores; +using Microsoft.Extensions.Logging; using Nest; namespace EventFlow.Elasticsearch.ReadStores @@ -42,13 +42,13 @@ public class ElasticsearchReadModelStore : IElasticsearchReadModelStore where TReadModel : class, IReadModel { - private readonly ILog _log; + private readonly ILogger> _log; private readonly IElasticClient _elasticClient; private readonly IReadModelDescriptionProvider _readModelDescriptionProvider; private readonly ITransientFaultHandler _transientFaultHandler; public ElasticsearchReadModelStore( - ILog log, + ILogger> log, IElasticClient elasticClient, IReadModelDescriptionProvider readModelDescriptionProvider, ITransientFaultHandler transientFaultHandler) @@ -65,7 +65,7 @@ public async Task> GetAsync( { var readModelDescription = _readModelDescriptionProvider.GetReadModelDescription(); - _log.Verbose(() => $"Fetching read model '{typeof(TReadModel).PrettyPrint()}' with ID '{id}' from index '{readModelDescription.IndexName}'"); + _log.LogTrace("Fetching read model '{TReadModel}' with ID '{Id}' from index '{IndexName}'", typeof(TReadModel).PrettyPrint(), id, readModelDescription.IndexName); var getResponse = await _elasticClient.GetAsync( id, @@ -108,9 +108,10 @@ public async Task DeleteAllAsync( { var readModelDescription = _readModelDescriptionProvider.GetReadModelDescription(); - _log.Information($"Deleting ALL '{typeof(TReadModel).PrettyPrint()}' by DELETING INDEX '{readModelDescription.IndexName}'!"); + _log.LogInformation("Deleting ALL '{TReadModel}' by DELETING INDEX '{IndexName}'!", typeof(TReadModel).PrettyPrint(), readModelDescription.IndexName); - var indices = _elasticClient.GetIndicesPointingToAlias(readModelDescription.IndexName.Value); + var indices = await _elasticClient.GetIndicesPointingToAliasAsync(readModelDescription.IndexName.Value) + .ConfigureAwait(false); foreach (var indexKey in indices) {