From 34fc81e3c86cfeea19c377292599f340063f31c5 Mon Sep 17 00:00:00 2001 From: ATCBot Date: Wed, 6 Jul 2022 07:44:36 +0000 Subject: [PATCH 01/23] Set version to '1.7-preview' --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 63fa435..aa44cc1 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.6-preview", + "version": "1.7-preview", "assemblyVersion": { "precision": "revision" }, From f833945ca697bcac4ec5322106feb37458cefa1e Mon Sep 17 00:00:00 2001 From: Lars Skovslund Date: Sun, 24 Jul 2022 09:33:53 +0200 Subject: [PATCH 02/23] WIP --- .../Converters/EventDataConverterPipeline.cs | 35 +++++ .../Converters/EventDocumentConverter.cs | 97 ++++++------ .../Converters/FaultedEventDataConverter.cs | 27 ++++ .../Converters/IEventDataConverter.cs | 26 ++++ .../Converters/NamedEventConverter.cs | 29 ++++ .../Converters/UnknownEventDataConverter.cs | 22 +++ .../Cosmos/CosmosEventSerializer.cs | 13 +- .../EventStoreClientOptions.cs | 6 + .../Events/EventBatchProducer.cs | 139 +++++++++--------- .../Events/EventCatalog.cs | 4 +- .../Events/EventDocument.cs | 27 ++-- .../Events/IEventTypeProvider.cs | 9 +- src/Atc.Cosmos.EventStore/FaultedEvent.cs | 13 ++ src/Atc.Cosmos.EventStore/IEventMetadata.cs | 5 + src/Atc.Cosmos.EventStore/UnknownEvent.cs | 9 ++ .../SampleEventTypeProvider.cs | 2 +- 16 files changed, 321 insertions(+), 142 deletions(-) create mode 100644 src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs create mode 100644 src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs create mode 100644 src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs create mode 100644 src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs create mode 100644 src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs create mode 100644 src/Atc.Cosmos.EventStore/FaultedEvent.cs create mode 100644 src/Atc.Cosmos.EventStore/UnknownEvent.cs diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs new file mode 100644 index 0000000..6327308 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +internal class EventDataConverterPipeline +{ + private readonly Func pipeline; + + public EventDataConverterPipeline( + IEnumerable converters) + { + Func next = (_, _, _) => null; + foreach (var c in converters.Reverse()) + { + next = (meta, data, options) => c.Convert( + meta, + data, + options, + () => next.Invoke(meta, data, options)); + } + + pipeline = next; + } + + public object Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options) + => pipeline.Invoke(metadata, data, options) + ?? new UnknownEvent( + data.GetRawText()); +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs b/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs index 6cc2fb2..1d44663 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs @@ -1,71 +1,72 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Converters +namespace Atc.Cosmos.EventStore.Converters; + +/// +/// Responsible for converting an event envelope to and from json without loosing underlying event type. +/// +internal class EventDocumentConverter : JsonConverter { - /// - /// Responsible for converting an event envelope to and from json without loosing underlying event type. - /// - internal class EventDocumentConverter : JsonConverter - { - private readonly IEventTypeProvider typeProvider; + private readonly EventDataConverterPipeline pipeline; + + public EventDocumentConverter( + EventDataConverterPipeline pipeline) + => this.pipeline = pipeline; - public EventDocumentConverter(IEventTypeProvider typeProvider) + public override EventDocument Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) { - this.typeProvider = typeProvider; + throw new JsonException(); } - public override EventDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException(); - } + using var jsonDocument = JsonDocument.ParseValue(ref reader); - using var jsonDocument = JsonDocument.ParseValue(ref reader); + // If we are reading the meta-data document, then skip it. + if (jsonDocument.RootElement.TryGetProperty(EventMetadataNames.Id, out var id) + && id.GetString() == StreamMetadata.StreamMetadataId) + { + return default!; + } - if (!jsonDocument.RootElement.TryGetProperty(EventMetadataNames.Properties, out var properties)) + if (jsonDocument.RootElement.TryGetProperty(EventMetadataNames.Properties, out var properties)) + { + var metadata = properties.Deserialize(options); + if (metadata is not null && jsonDocument.RootElement.TryGetProperty(EventMetadataNames.Data, out var data)) { - // If we are reading the meta-data document, then skip it. - if (jsonDocument.RootElement.TryGetProperty(EventMetadataNames.Id, out var id) - && id.GetString() == StreamMetadata.StreamMetadataId) - { - return default!; - } + var typeData = pipeline + .Convert(metadata, data, options); - throw new JsonException(); + return new EventDocument(typeData, metadata); } - - if (!properties.TryGetProperty(EventMetadataNames.EventName, out var name)) - { - throw new JsonException(); - } - - var result = (EventDocument)JsonSerializer - .Deserialize( - jsonDocument.RootElement.GetRawText(), - MakeGenericEventDocumentType( - typeProvider.GetEventType( - name.GetString() ?? string.Empty)), - options)!; - - return result; } - public override void Write(Utf8JsonWriter writer, EventDocument value, JsonSerializerOptions options) - { - if (value is not EventDocument evt) - { - throw new ArgumentException("Value to write must be of type Event.", nameof(value)); - } + return new EventDocument( + new FaultedEvent( + jsonDocument.RootElement.GetRawText(), + null), + new EventMetadata()); + } - JsonSerializer.Serialize(writer, evt, options); + public override void Write( + Utf8JsonWriter writer, + EventDocument value, + JsonSerializerOptions options) + { + if (value is not EventDocument evt) + { + throw new ArgumentException("Value to write must be of type Event.", nameof(value)); } - private static Type MakeGenericEventDocumentType(Type type) - => typeof(EventDocument<>).MakeGenericType(type); + JsonSerializer.Serialize(writer, evt, options); } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs new file mode 100644 index 0000000..bd35160 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +internal class FaultedEventDataConverter : IEventDataConverter +{ + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "ByDesign")] + public object? Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options, + Func next) + { + try + { + return next.Invoke(); + } + catch (Exception ex) + { + return new FaultedEvent( + data.GetRawText(), + ex); + } + } +} diff --git a/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs new file mode 100644 index 0000000..6cd274b --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +/// +/// Represent a convert responsible for converting an event data json element into a CLR object. +/// +public interface IEventDataConverter +{ + /// + /// Convert to an object. + /// + /// Event metadata information. + /// Event data to convert. + /// Json serlization options to use. + /// Delegate for parsing the convertion on to the next in the pipeline. + /// The converted object or null. + [SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "ByDesign")] + object? Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options, + Func next); +} diff --git a/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs new file mode 100644 index 0000000..306e0b4 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Text.Json; +using Atc.Cosmos.EventStore.Events; + +namespace Atc.Cosmos.EventStore.Converters; + +/// +/// Responsible for converting from a named event to a CLI type. +/// +internal class NamedEventConverter : IEventDataConverter +{ + private readonly IEventTypeProvider typeProvider; + + public NamedEventConverter( + IEventTypeProvider typeProvider) + => this.typeProvider = typeProvider; + + public object? Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options, + Func next) + => typeProvider.GetEventType(metadata.Name) + switch + { + { } type => data.Deserialize(type, options), + _ => next.Invoke(), + }; +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs new file mode 100644 index 0000000..9864b94 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +/// +/// Responsible for converting the data to raw json string. +/// +internal class UnknownEventDataConverter : IEventDataConverter +{ + public object? Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options, + Func next) + => next.Invoke() + switch + { + { } converted => converted, + _ => new UnknownEvent(data.GetRawText()), + }; +} diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs index b820b42..35c2793 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Atc.Cosmos.EventStore.Converters; @@ -11,7 +12,7 @@ namespace Atc.Cosmos.EventStore.Cosmos { /// - /// EventStore cosmos JSON serializer implementation for System.Text.Json. + /// EventStore cosmos JSON serializer implementation for . /// internal class CosmosEventSerializer : CosmosSerializer { @@ -28,7 +29,15 @@ public CosmosEventSerializer( PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - jsonSerializerOptions.Converters.Add(new EventDocumentConverter(typeProvider)); + var pipeline = new EventDataConverterPipeline( + new IEventDataConverter[] + { + new FaultedEventDataConverter(), + new UnknownEventDataConverter(), + }.Concat(options.Value.EventDataConverter) + .Concat(new[] { new NamedEventConverter(typeProvider) })); + + jsonSerializerOptions.Converters.Add(new EventDocumentConverter(pipeline)); jsonSerializerOptions.Converters.Add(new TimeSpanConverter()); jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); jsonSerializerOptions.Converters.Add(new StreamIdConverter()); diff --git a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs index 3db6383..51bed92 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Atc.Cosmos.EventStore.Converters; using Azure.Core; using Microsoft.Azure.Cosmos; @@ -50,6 +51,11 @@ public string? ConnectionString /// public ICollection CustomJsonConverter { get; } = new List(); + /// + /// Gets collections of custom event data converters. + /// + public ICollection EventDataConverter { get; } = new List(); + public string? Endpoint { get; private set; } public string? AuthKey { get; private set; } diff --git a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs index 9ae67fb..5423106 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs @@ -3,84 +3,83 @@ using System.Linq; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +internal class EventBatchProducer : IEventBatchProducer { - internal class EventBatchProducer : IEventBatchProducer + private readonly IDateTimeProvider dateTimeProvider; + private readonly IEventIdProvider eventIdProvider; + private readonly IEventNameProvider nameProvider; + + public EventBatchProducer( + IDateTimeProvider dateTimeProvider, + IEventIdProvider eventIdProvider, + IEventNameProvider nameProvider) { - private readonly IDateTimeProvider dateTimeProvider; - private readonly IEventIdProvider eventIdProvider; - private readonly IEventNameProvider nameProvider; + this.dateTimeProvider = dateTimeProvider; + this.eventIdProvider = eventIdProvider; + this.nameProvider = nameProvider; + } - public EventBatchProducer( - IDateTimeProvider dateTimeProvider, - IEventIdProvider eventIdProvider, - IEventNameProvider nameProvider) - { - this.dateTimeProvider = dateTimeProvider; - this.eventIdProvider = eventIdProvider; - this.nameProvider = nameProvider; - } + public StreamBatch FromEvents( + IReadOnlyCollection events, + IStreamMetadata metadata, + StreamWriteOptions? options) + { + var timestamp = dateTimeProvider.GetDateTime(); + var version = metadata.Version.Value; - public StreamBatch FromEvents( - IReadOnlyCollection events, - IStreamMetadata metadata, - StreamWriteOptions? options) - { - var timestamp = dateTimeProvider.GetDateTime(); - var version = metadata.Version.Value; + var documents = events + .Select(evt => Convert( + evt, + metadata, + ++version, // increment version for event + options?.CorrelationId, + options?.CausationId, + timestamp)) + .ToArray(); - var documents = events - .Select(evt => Convert( - evt, - metadata, - ++version, // increment version for event - options?.CorrelationId, - options?.CausationId, - timestamp)) - .ToArray(); + return new StreamBatch( + new StreamMetadata( + StreamMetadata.StreamMetadataId, + metadata.StreamId.Value, + metadata.StreamId, + version, + StreamState.Active, + timestamp) + { + ETag = metadata.ETag, + }, + documents); + } - return new StreamBatch( - new StreamMetadata( - StreamMetadata.StreamMetadataId, - metadata.StreamId.Value, - metadata.StreamId, - version, - StreamState.Active, - timestamp) - { - ETag = metadata.ETag, - }, - documents); - } + private EventDocument Convert( + object evt, + IStreamMetadata metadata, + long version, + string? correlationId, + string? causationId, + DateTimeOffset timestamp) + { + var eventId = eventIdProvider.CreateUniqueId(metadata); + var streamId = metadata.StreamId.Value; + var name = nameProvider.GetName(evt); - private EventDocument Convert( - object evt, - IStreamMetadata metadata, - long version, - string? correlationId, - string? causationId, - DateTimeOffset timestamp) + return new EventDocument { - var eventId = eventIdProvider.CreateUniqueId(metadata); - var streamId = metadata.StreamId.Value; - var name = nameProvider.GetName(evt); - - return new EventDocument + Id = eventId, + PartitionKey = streamId, + Data = evt, + Properties = new EventMetadata { - Id = eventId, - PartitionKey = streamId, - Data = evt, - Properties = new EventMetadata - { - CausationId = causationId, - CorrelationId = correlationId, - EventId = eventId, - StreamId = streamId, - Version = version, - Timestamp = timestamp, - Name = name, - }, - }; - } + CausationId = causationId, + CorrelationId = correlationId, + EventId = eventId, + StreamId = streamId, + Version = version, + Timestamp = timestamp, + Name = name, + }, + }; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs b/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs index 0d75781..0220b10 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs @@ -16,10 +16,10 @@ internal class EventCatalog : IEventCatalog public EventCatalog(IReadOnlyDictionary mappings) => this.mappings = mappings; - public Type GetEventType(EventName name) + public Type? GetEventType(EventName name) => mappings.TryGetValue(name, out var type) ? type - : throw new EventNotRegisteredException((string)name); + : default; public string GetName(object evt) { diff --git a/src/Atc.Cosmos.EventStore/Events/EventDocument.cs b/src/Atc.Cosmos.EventStore/Events/EventDocument.cs index ad967d6..7c5a83f 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventDocument.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventDocument.cs @@ -1,22 +1,21 @@ using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +internal abstract class EventDocument : IEvent { - internal abstract class EventDocument : IEvent - { - [JsonPropertyName(EventMetadataNames.Id)] - public string Id { get; set; } = string.Empty; + [JsonPropertyName(EventMetadataNames.Id)] + public string Id { get; set; } = string.Empty; - [JsonPropertyName(EventMetadataNames.PartitionKey)] - public string PartitionKey { get; set; } = string.Empty; + [JsonPropertyName(EventMetadataNames.PartitionKey)] + public string PartitionKey { get; set; } = string.Empty; - [JsonPropertyName(EventMetadataNames.Properties)] - public EventMetadata Properties { get; set; } = new EventMetadata(); + [JsonPropertyName(EventMetadataNames.Properties)] + public EventMetadata Properties { get; set; } = new EventMetadata(); - [JsonIgnore] - public virtual object Data { get; set; } = new object(); + [JsonIgnore] + public virtual object Data { get; set; } = new object(); - [JsonIgnore] - IEventMetadata IEvent.Metadata => Properties; - } + [JsonIgnore] + IEventMetadata IEvent.Metadata => Properties; } diff --git a/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs b/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs index 1082a13..b4673c8 100644 --- a/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs +++ b/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs @@ -1,9 +1,8 @@ using System; -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +public interface IEventTypeProvider { - public interface IEventTypeProvider - { - Type GetEventType(EventName name); - } + Type? GetEventType(EventName name); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/FaultedEvent.cs b/src/Atc.Cosmos.EventStore/FaultedEvent.cs new file mode 100644 index 0000000..b1703a5 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/FaultedEvent.cs @@ -0,0 +1,13 @@ +using System; + +namespace Atc.Cosmos.EventStore; + +/// +/// Represents a faulted event read from the stream. +/// +/// Inspect json and exception info to gain more insights. +/// Json that failed to deserialize. +/// Exception details on failure. +public record FaultedEvent( + string Json, + Exception? Exception); diff --git a/src/Atc.Cosmos.EventStore/IEventMetadata.cs b/src/Atc.Cosmos.EventStore/IEventMetadata.cs index a6010eb..3ecd9e0 100644 --- a/src/Atc.Cosmos.EventStore/IEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore/IEventMetadata.cs @@ -12,6 +12,11 @@ public interface IEventMetadata /// string EventId { get; } + /// + /// Gets the name of the event. + /// + string Name { get; } + /// /// Gets the correlation id associated with the event. /// diff --git a/src/Atc.Cosmos.EventStore/UnknownEvent.cs b/src/Atc.Cosmos.EventStore/UnknownEvent.cs new file mode 100644 index 0000000..93f69d9 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/UnknownEvent.cs @@ -0,0 +1,9 @@ +namespace Atc.Cosmos.EventStore; + +/// +/// Represents an unknown event read from stream. +/// +/// Inspect metadata to identify the unknown event name. +/// Event data json. +public record UnknownEvent( + string Json); diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs index f961aa8..96bd994 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs @@ -5,6 +5,6 @@ namespace Atc.Cosmos.EventStore.IntegrationTests { public class SampleEventTypeProvider : IEventTypeProvider { - public Type GetEventType(EventName name) => typeof(SampleEvent); + public Type? GetEventType(EventName name) => typeof(SampleEvent); } } From 11c774ee4185e807d4956e89a50b846fa346268c Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Thu, 17 Nov 2022 15:49:22 +0100 Subject: [PATCH 03/23] Update dependencies --- Directory.Build.props | 4 ++-- src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj | 6 +++--- src/Directory.Build.props | 2 +- .../Atc.Cosmos.EventStore.Cqrs.Tests.csproj | 6 +++--- .../Atc.Cosmos.EventStore.IntegrationTests.csproj | 6 +++--- .../Atc.Cosmos.EventStore.Tests.csproj | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index ef86461..461afe5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,9 +39,9 @@ - + - + \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj index 7768b20..e20899a 100644 --- a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj +++ b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj @@ -13,11 +13,11 @@ - + - + - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cb2027c..5e72922 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -55,7 +55,7 @@ - + diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj index ac52c0b..beb3ef1 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj @@ -6,10 +6,10 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj index 8015ae6..f75352e 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj @@ -6,10 +6,10 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj index 8015ae6..f75352e 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj @@ -6,10 +6,10 @@ - + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 777e58df5c6fa3cf6f1d7cb8dca34594d0ec0f0e Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Thu, 17 Nov 2022 15:51:16 +0100 Subject: [PATCH 04/23] Introduce event data converter pipeline --- .../Converters/EventDataConverterPipeline.cs | 20 +----- .../EventDataConverterPipelineBuilder.cs | 46 ++++++++++++++ .../Cosmos/CosmosEventSerializer.cs | 18 +++--- .../Cosmos/IEventStoreInitializer.cs | 14 +++-- .../IEventStoreClient.cs | 6 +- .../EventDataConverterPipelineBuilderTests.cs | 31 ++++++++++ .../FaultedEventDataConverterTests.cs | 49 +++++++++++++++ .../Converters/NamedEventConverterTests.cs | 61 +++++++++++++++++++ .../UnknownEventDataConverterTests.cs | 44 +++++++++++++ .../Events/EventCatalogTests.cs | 8 +-- .../Fakes/EventOne.cs | 11 ++-- .../Fakes/FakeEventDataConverter.cs | 21 +++++++ 12 files changed, 283 insertions(+), 46 deletions(-) create mode 100644 src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs create mode 100644 test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs index 6327308..ae3bec2 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; @@ -10,20 +8,8 @@ internal class EventDataConverterPipeline private readonly Func pipeline; public EventDataConverterPipeline( - IEnumerable converters) - { - Func next = (_, _, _) => null; - foreach (var c in converters.Reverse()) - { - next = (meta, data, options) => c.Convert( - meta, - data, - options, - () => next.Invoke(meta, data, options)); - } - - pipeline = next; - } + Func pipeline) + => this.pipeline = pipeline; public object Convert( IEventMetadata metadata, @@ -32,4 +18,4 @@ public object Convert( => pipeline.Invoke(metadata, data, options) ?? new UnknownEvent( data.GetRawText()); -} \ No newline at end of file +} diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs new file mode 100644 index 0000000..c24ebc3 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +internal class EventDataConverterPipelineBuilder +{ + private readonly Collection converters = new(); + + public EventDataConverterPipelineBuilder AddConverter( + IEventDataConverter converter) + { + converters.Add(converter); + + return this; + } + + public EventDataConverterPipelineBuilder AddConverters( + IEnumerable converters) + { + foreach (var converter in converters) + { + this.converters.Add(converter); + } + + return this; + } + + public EventDataConverterPipeline Build() + { + Func next = (_, _, _) => null; + foreach (var c in converters.Reverse()) + { + next = (meta, data, options) => c.Convert( + meta, + data, + options, + () => next.Invoke(meta, data, options)); + } + + return new EventDataConverterPipeline(next); + } +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs index 35c2793..7f8eb73 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Atc.Cosmos.EventStore.Converters; @@ -29,19 +28,18 @@ public CosmosEventSerializer( PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - var pipeline = new EventDataConverterPipeline( - new IEventDataConverter[] - { - new FaultedEventDataConverter(), - new UnknownEventDataConverter(), - }.Concat(options.Value.EventDataConverter) - .Concat(new[] { new NamedEventConverter(typeProvider) })); - - jsonSerializerOptions.Converters.Add(new EventDocumentConverter(pipeline)); jsonSerializerOptions.Converters.Add(new TimeSpanConverter()); jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); jsonSerializerOptions.Converters.Add(new StreamIdConverter()); jsonSerializerOptions.Converters.Add(new StreamVersionConverter()); + jsonSerializerOptions.Converters.Add( + new EventDocumentConverter( + new EventDataConverterPipelineBuilder() + .AddConverter(new FaultedEventDataConverter()) + .AddConverter(new UnknownEventDataConverter()) + .AddConverters(options.Value.EventDataConverter) + .AddConverter(new NamedEventConverter(typeProvider)) + .Build())); foreach (var converter in options.Value.CustomJsonConverter) { diff --git a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs index e48e8ba..ebbd6a2 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs @@ -2,12 +2,14 @@ using System.Threading.Tasks; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +public interface IEventStoreInitializer { - internal interface IEventStoreInitializer - { - Task CreateEventStoreAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken); + Task CreateEventStoreAsync( + ThroughputProperties throughputProperties, + CancellationToken cancellationToken); - void CreateEventStore(ThroughputProperties throughputProperties); - } + void CreateEventStore( + ThroughputProperties throughputProperties); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IEventStoreClient.cs b/src/Atc.Cosmos.EventStore/IEventStoreClient.cs index c3314d0..863f639 100644 --- a/src/Atc.Cosmos.EventStore/IEventStoreClient.cs +++ b/src/Atc.Cosmos.EventStore/IEventStoreClient.cs @@ -97,11 +97,11 @@ IAsyncEnumerable QueryStreamsAsync( /// /// /// Only one checkpoint per name can exists at any given time. - /// A checkpoint will be overriden when using an existing name. + /// A checkpoint will be overridden when using an existing name. /// /// Name of checkpoint. /// Id of stream. - /// Version within the stream this chackpoint is related too. + /// Version within the stream this checkpoint is related too. /// (Optional) State object to store along side the checkpoint. /// (Optional) representing request cancellation. /// A representing the asynchronous operation. @@ -126,7 +126,7 @@ Task SetStreamCheckpointAsync( CancellationToken cancellationToken = default); /// - /// Gets a named chackpoint from a stream. + /// Gets a named checkpoint from a stream. /// /// Name of checkpoint. /// Id of stream. diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs new file mode 100644 index 0000000..ddac9f2 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs @@ -0,0 +1,31 @@ +using System.Linq; +using System.Text.Json; +using Atc.Cosmos.EventStore.Converters; +using Atc.Cosmos.EventStore.Tests.Fakes; +using Atc.Test; +using FluentAssertions; +using Xunit; + +namespace Atc.Cosmos.EventStore.Tests.Converters; + +public class EventDataConverterPipelineBuilderTests +{ + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + + [Theory, AutoNSubstituteData] + internal void Should_Call_All_Converters_InReverseOrder( + IEventMetadata metadata, + JsonSerializerOptions options, + FakeEventDataConverter[] converters, + FakeEventDataConverter converter, + EventDataConverterPipelineBuilder sut) + { + sut + .AddConverter(converter) + .AddConverters(converters) + .Build() + .Convert(metadata, doc.RootElement, options) + .Should() + .Be(string.Join(string.Empty, new[] { converter }.Concat(converters).Select(c => c.Val))); + } +} diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs new file mode 100644 index 0000000..fed40bd --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Atc.Cosmos.EventStore.Converters; +using Atc.Cosmos.EventStore.Tests.Fakes; +using Atc.Test; +using FluentAssertions; +using Xunit; + +namespace Atc.Cosmos.EventStore.Tests.Converters +{ + public class FaultedEventDataConverterTests + { + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + + [Theory, AutoNSubstituteData] + internal void Should_Return_Converted_Value( + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + FaultedEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); + + [Theory, AutoNSubstituteData] + internal void Should_Return_FaultedEvent_When_Exception_IsThrown( + IEventMetadata metadata, + JsonSerializerOptions options, + KeyNotFoundException exception, + FaultedEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => throw exception) + .Should() + .BeEquivalentTo( + new FaultedEvent( + doc.RootElement.GetRawText(), + exception)); + } +} diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs new file mode 100644 index 0000000..4e5a1e5 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Text.Json; +using Atc.Cosmos.EventStore.Converters; +using Atc.Cosmos.EventStore.Events; +using Atc.Cosmos.EventStore.Tests.Fakes; +using Atc.Test; +using AutoFixture.Xunit2; +using FluentAssertions; +using NSubstitute; +using Xunit; + +namespace Atc.Cosmos.EventStore.Tests.Converters +{ + public class NamedEventConverterTests + { + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + + [Theory, AutoNSubstituteData] + internal void Should_Return_Value_FromNext_When_TypeName_IsNotFound( + [Frozen] IEventTypeProvider typeProvider, + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + NamedEventConverter sut) + { + typeProvider + .GetEventType(default) + .ReturnsForAnyArgs((Type)null); + + sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); + } + + [Theory, AutoNSubstituteData] + internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( + [Frozen] IEventTypeProvider typeProvider, + IEventMetadata metadata, + NamedEventConverter sut) + { + typeProvider + .GetEventType(metadata.Name) + .ReturnsForAnyArgs(typeof(EventOne)); + + sut + .Convert( + metadata, + doc.RootElement, + new JsonSerializerOptions(), + () => null) + .Should() + .BeEquivalentTo( + new EventOne("name", 42)); + } + } +} diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs new file mode 100644 index 0000000..950012f --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs @@ -0,0 +1,44 @@ +using System.Text.Json; +using Atc.Cosmos.EventStore.Converters; +using Atc.Cosmos.EventStore.Tests.Fakes; +using Atc.Test; +using FluentAssertions; +using Xunit; + +namespace Atc.Cosmos.EventStore.Tests.Converters +{ + public class UnknownEventDataConverterTests + { + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + + [Theory, AutoNSubstituteData] + internal void Should_Return_Converted_Value_Id_NotNull( + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + UnknownEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); + + [Theory, AutoNSubstituteData] + internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( + IEventMetadata metadata, + JsonSerializerOptions options, + UnknownEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => null) + .Should() + .BeEquivalentTo( + new UnknownEvent(doc.RootElement.GetRawText())); + } +} diff --git a/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs b/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs index 43882c2..6208701 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs @@ -21,12 +21,12 @@ internal void Should_Resolve_Type_From_Name( .Be(mappings[mappings.Keys.First()]); [Theory, AutoNSubstituteData] - internal void ShouldThrow_When_Name_IsNotFound( + internal void ShouldReturn_Null_When_Name_IsNotFound( EventCatalog sut) - => FluentActions - .Invoking(() => sut.GetEventType("non-existing-name")) + => sut + .GetEventType("non-existing-name") .Should() - .Throw(); + .BeNull(); [Theory, AutoNSubstituteData] public void Should_Resolve_Name_From_Type( diff --git a/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs b/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs index af6b687..435fc55 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs @@ -1,10 +1,9 @@ using System; -namespace Atc.Cosmos.EventStore.Tests.Fakes -{ - public record EventOne(string Name, int Number); +namespace Atc.Cosmos.EventStore.Tests.Fakes; - public record EventTwo(string Name, DateTimeOffset Timestamp); +public record EventOne(string Name, int Number); - public record EventThree(string Name); -} \ No newline at end of file +public record EventTwo(string Name, DateTimeOffset Timestamp); + +public record EventThree(string Name); diff --git a/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs b/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs new file mode 100644 index 0000000..98d7fbd --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs @@ -0,0 +1,21 @@ +using System; +using System.Text.Json; +using Atc.Cosmos.EventStore.Converters; + +namespace Atc.Cosmos.EventStore.Tests.Fakes; + +public class FakeEventDataConverter : IEventDataConverter +{ + public FakeEventDataConverter( + string val) + => Val = val; + + public string Val { get; } + + public object? Convert( + IEventMetadata metadata, + JsonElement data, + JsonSerializerOptions options, + Func next) + => Val + next.Invoke(); +} From bd68f8e84e67354eb755976517a11fce9baa1917 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Thu, 17 Nov 2022 15:54:04 +0100 Subject: [PATCH 05/23] Improve analyzer rules --- test/.editorconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/.editorconfig b/test/.editorconfig index b917ffa..2ac04b4 100644 --- a/test/.editorconfig +++ b/test/.editorconfig @@ -55,4 +55,5 @@ dotnet_diagnostic.CA1812.severity = none # Test classes used as gener dotnet_diagnostic.SA1202.severity = none # Private helper methods makes sense to keep at top of test classes, as tests are added to bottom. dotnet_diagnostic.CA2201.severity = none # Instantiating Exceptions as test data should be allowed. dotnet_diagnostic.MA0016.severity = none # Prefer return collection abstraction instead of implementation -dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists \ No newline at end of file +dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists +dotnet_diagnostic.CA1711.severity = none # Identifiers should not have incorrect suffix \ No newline at end of file From b7667d824de596e768cc0308e3ffa644f454013a Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Thu, 17 Nov 2022 15:54:17 +0100 Subject: [PATCH 06/23] Update change log --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e7363f..a86cd4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Pipeline for controlling event data convertion `IEventDataConverter` +- Added custom event data converters to be configured using `EventStoreOptions`. This will enable scenarioes such as converting from one version of an event to another. +- Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` but using well known types `FaultedEvent` and `UnknownEvent`. + +### Removed +- Setting `ConfigurationString` when configuring event store options. + ## [1.6.8] - 2022-07-06 ### Added From 3197ec06427d3a123544477c0daf41aafbc2aa1b Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 08:36:22 +0100 Subject: [PATCH 07/23] Introduce event data converter pipeline executor --- .../Converters/EventDataConverterExecutor.cs | 27 +++++++++++++++++++ .../Converters/EventDataConverterPipeline.cs | 7 +++-- .../EventDataConverterPipelineBuilder.cs | 17 +++++------- .../Converters/FaultedEventDataConverter.cs | 2 -- .../IEventDataConverterPipelineExecutor.cs | 11 ++++++++ .../Converters/NamedEventConverter.cs | 2 +- .../EventDataConverterPipelineBuilderTests.cs | 4 +-- 7 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 src/Atc.Cosmos.EventStore/Converters/EventDataConverterExecutor.cs create mode 100644 src/Atc.Cosmos.EventStore/Converters/IEventDataConverterPipelineExecutor.cs diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterExecutor.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterExecutor.cs new file mode 100644 index 0000000..0319d91 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterExecutor.cs @@ -0,0 +1,27 @@ +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +internal class EventDataConverterExecutor : IEventDataConverterPipelineExecutor +{ + private readonly IEventDataConverter converter; + private readonly IEventDataConverterPipelineExecutor? next; + + public EventDataConverterExecutor( + IEventDataConverter converter, + IEventDataConverterPipelineExecutor? next) + { + this.converter = converter; + this.next = next; + } + + public object? Execute( + IEventMetadata metadata, + JsonElement json, + JsonSerializerOptions options) + => converter.Convert( + metadata, + json, + options, + () => next?.Execute(metadata, json, options)); +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs index ae3bec2..0168b99 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipeline.cs @@ -1,21 +1,20 @@ -using System; using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; internal class EventDataConverterPipeline { - private readonly Func pipeline; + private readonly IEventDataConverterPipelineExecutor? pipeline; public EventDataConverterPipeline( - Func pipeline) + IEventDataConverterPipelineExecutor? pipeline) => this.pipeline = pipeline; public object Convert( IEventMetadata metadata, JsonElement data, JsonSerializerOptions options) - => pipeline.Invoke(metadata, data, options) + => pipeline?.Execute(metadata, data, options) ?? new UnknownEvent( data.GetRawText()); } diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs index c24ebc3..0ef7c06 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs @@ -1,8 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; @@ -31,16 +30,12 @@ public EventDataConverterPipelineBuilder AddConverters( public EventDataConverterPipeline Build() { - Func next = (_, _, _) => null; - foreach (var c in converters.Reverse()) + IEventDataConverterPipelineExecutor? pipeline = null; + foreach (var converter in converters.Reverse()) { - next = (meta, data, options) => c.Convert( - meta, - data, - options, - () => next.Invoke(meta, data, options)); + pipeline = new EventDataConverterExecutor(converter, pipeline); } - return new EventDataConverterPipeline(next); + return new EventDataConverterPipeline(pipeline); } -} \ No newline at end of file +} diff --git a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs index bd35160..6cfeb77 100644 --- a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs @@ -1,12 +1,10 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; internal class FaultedEventDataConverter : IEventDataConverter { - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "ByDesign")] public object? Convert( IEventMetadata metadata, JsonElement data, diff --git a/src/Atc.Cosmos.EventStore/Converters/IEventDataConverterPipelineExecutor.cs b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverterPipelineExecutor.cs new file mode 100644 index 0000000..d57da88 --- /dev/null +++ b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverterPipelineExecutor.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace Atc.Cosmos.EventStore.Converters; + +internal interface IEventDataConverterPipelineExecutor +{ + object? Execute( + IEventMetadata metadata, + JsonElement json, + JsonSerializerOptions options); +} diff --git a/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs index 306e0b4..fd03357 100644 --- a/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs @@ -5,7 +5,7 @@ namespace Atc.Cosmos.EventStore.Converters; /// -/// Responsible for converting from a named event to a CLI type. +/// Responsible for converting from a named event to a type. /// internal class NamedEventConverter : IEventDataConverter { diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs index ddac9f2..eebf34b 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs @@ -19,13 +19,11 @@ internal void Should_Call_All_Converters_InReverseOrder( FakeEventDataConverter[] converters, FakeEventDataConverter converter, EventDataConverterPipelineBuilder sut) - { - sut + => sut .AddConverter(converter) .AddConverters(converters) .Build() .Convert(metadata, doc.RootElement, options) .Should() .Be(string.Join(string.Empty, new[] { converter }.Concat(converters).Select(c => c.Val))); - } } From 33f458569a905d5036f2a958015044789f854ab9 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 08:37:34 +0100 Subject: [PATCH 08/23] Upgrade dependency to Microsoft.Azure.Cosmos (3.31.0->3.31.2) --- src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj index e20899a..54ac0d3 100644 --- a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj +++ b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj @@ -13,7 +13,7 @@ - + From 05c25e0533a8383c469f876747aad9cdab86bcbd Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 08:42:59 +0100 Subject: [PATCH 09/23] Reintroduce code analysis suppression --- .../Converters/FaultedEventDataConverter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs index 6cfeb77..bd35160 100644 --- a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; internal class FaultedEventDataConverter : IEventDataConverter { + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "ByDesign")] public object? Convert( IEventMetadata metadata, JsonElement data, From e2d51e8ef8400e4a40cc2ff973419248d1d61b61 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 08:53:23 +0100 Subject: [PATCH 10/23] Update dependencies and move to c#11 --- Directory.Build.props | 4 ++-- src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj | 8 ++++---- src/Directory.Build.props | 2 +- .../Atc.Cosmos.EventStore.Cqrs.Tests.csproj | 6 +++--- .../Atc.Cosmos.EventStore.IntegrationTests.csproj | 6 +++--- .../Atc.Cosmos.EventStore.Tests.csproj | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 461afe5..83e6bc2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ enable - 10.0 + 11.0 true 1573,1591,1712,CA1014 @@ -41,7 +41,7 @@ - + \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj index 54ac0d3..06e99fe 100644 --- a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj +++ b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj @@ -14,10 +14,10 @@ - - - - + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 5e72922..f4de6d3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -55,7 +55,7 @@ - + diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj index beb3ef1..35a1a46 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj @@ -7,14 +7,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj index f75352e..27655dc 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj @@ -7,14 +7,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj index f75352e..27655dc 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj @@ -7,14 +7,14 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 5ecbc4939ac326cb7cb9462ecf85c20df77e2d01 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 09:37:38 +0100 Subject: [PATCH 11/23] Enforce use of file scoped namespaces --- .editorconfig | 4 ++++ Directory.Build.props | 1 + .../Atc.Cosmos.EventStore.Cqrs.csproj | 4 ++++ src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj | 4 ++++ src/Directory.Build.props | 2 +- .../Atc.Cosmos.EventStore.Cqrs.Tests.csproj | 4 ++++ .../Atc.Cosmos.EventStore.IntegrationTests.csproj | 4 ++++ .../Atc.Cosmos.EventStore.Tests.csproj | 4 ++++ test/Directory.Build.props | 4 ++++ 9 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 2554a61..7a5a60e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -145,6 +145,10 @@ csharp_style_var_for_built_in_types = true csharp_style_var_when_type_is_apparent = true # IDE0007 and IDE0008 csharp_style_var_elsewhere = true # IDE0007 and IDE0008 +# Namespace declaration preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0160-ide0161?view=vs-2022 +csharp_style_namespace_declarations = file_scoped # IDE0160 and IDE0161 + # Expression-bodied members # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members csharp_style_expression_bodied_constructors = when_on_single_line # IDE0021 diff --git a/Directory.Build.props b/Directory.Build.props index 83e6bc2..0efd766 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,6 +17,7 @@ enable 11.0 + enable true 1573,1591,1712,CA1014 diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Atc.Cosmos.EventStore.Cqrs.csproj b/src/Atc.Cosmos.EventStore.Cqrs/Atc.Cosmos.EventStore.Cqrs.csproj index 3c532a2..9eb59c3 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Atc.Cosmos.EventStore.Cqrs.csproj +++ b/src/Atc.Cosmos.EventStore.Cqrs/Atc.Cosmos.EventStore.Cqrs.csproj @@ -11,6 +11,10 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + diff --git a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj index 06e99fe..dd39bd0 100644 --- a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj +++ b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj @@ -11,6 +11,10 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f4de6d3..338c7ce 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -19,7 +19,7 @@ - + Library netstandard2.1 diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj index 35a1a46..a97325e 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj index 27655dc..f00fdcb 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj index 27655dc..f00fdcb 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 9986f29..e56f87b 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -15,4 +15,8 @@ + + + + \ No newline at end of file From 51a3f69256d8c152b4a5351977bf64433f6767d9 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 09:38:28 +0100 Subject: [PATCH 12/23] Cleanup code (namespacing and usings) --- src/Atc.Cosmos.EventStore.Cqrs/CommandBase.cs | 34 +- .../CommandContextExtensions.cs | 40 +- .../CommandResult.cs | 15 +- .../CommandResultExtensions.cs | 11 +- .../Commands/CommandContext.cs | 20 +- .../Commands/CommandHandlerMetadata.cs | 33 +- .../Commands/CommandProcessor.cs | 164 ++++--- .../Commands/CommandProcessorFactory.cs | 26 +- .../Commands/ICommandHandlerMetadata.cs | 22 +- .../Commands/IStateProjector.cs | 18 +- .../Commands/IStateWriter.cs | 19 +- .../Commands/IStreamState.cs | 11 +- .../Commands/StateProjector.cs | 109 +++-- .../Commands/StateWriter.cs | 121 +++-- .../Commands/StreamState.cs | 11 +- .../EventCatalogBuilderExtensions.cs | 139 +++--- .../EventStoreOptionsBuilderExtensions.cs | 38 +- .../IEventStoreCqrsBuilder.cs | 31 +- .../DependencyInjection/IProjectionBuilder.cs | 23 +- .../Internal/EventStoreCqrsBuilder.cs | 171 ++++--- .../Internal/ProjectionBuilder.cs | 94 ++-- .../Diagnostics/IProjectionDiagnostics.cs | 22 +- .../IProjectionProcessOperation.cs | 22 +- .../Diagnostics/ProjectionDiagnostics.cs | 39 +- .../Diagnostics/ProjectionProcessOperation.cs | 36 +- .../EventMetadata.cs | 19 +- .../EventStreamId.cs | 42 +- .../EventStreamVersion.cs | 97 ++-- .../IAggregateProjection.cs | 16 +- src/Atc.Cosmos.EventStore.Cqrs/ICommand.cs | 57 ++- .../ICommandContext.cs | 11 +- .../ICommandHandler.cs | 18 +- .../ICommandProcessor.cs | 16 +- .../ICommandProcessorFactory.cs | 11 +- .../IConsumeEvent.cs | 9 +- .../IConsumeEventAsync.cs | 16 +- src/Atc.Cosmos.EventStore.Cqrs/IProjection.cs | 25 +- .../Internal/ConsumeEventMetadata.cs | 130 +++--- .../Internal/DependencyInitializer.cs | 20 +- .../Internal/DependencyInitializerJob.cs | 25 +- .../Internal/IDependencyInitializer.cs | 9 +- src/Atc.Cosmos.EventStore.Cqrs/OnConflict.cs | 33 +- .../ProjectionAction.cs | 11 +- .../ProjectionFilterAttribute.cs | 19 +- .../Projections/IProjectionFactory.cs | 15 +- .../Projections/IProjectionJob.cs | 23 +- .../Projections/IProjectionMetadata.cs | 27 +- .../Projections/IProjectionOptions.cs | 13 +- .../Projections/IProjectionOptionsFactory.cs | 11 +- .../Projections/IProjectionProcessor.cs | 26 +- .../Projections/ProjectionFactory.cs | 26 +- .../Projections/ProjectionFilter.cs | 79 ++-- .../Projections/ProjectionJob.cs | 100 ++-- .../Projections/ProjectionMetadata.cs | 49 +- .../Projections/ProjectionOptions.cs | 31 +- .../Projections/ProjectionOptionsFactory.cs | 27 +- .../Projections/ProjectionProcessor.cs | 152 +++--- src/Atc.Cosmos.EventStore.Cqrs/ResultType.cs | 47 +- .../StreamEventAttribute.cs | 17 +- .../Testing/CommandHandlerExtensions.cs | 19 +- .../Testing/CommandHandlerTester.cs | 211 +++++---- .../Testing/ICommandContextInspector.cs | 11 +- .../Testing/ICommandGiven.cs | 11 +- .../Testing/ICommandThen.cs | 29 +- .../Testing/ICommandWhen.cs | 11 +- src/Atc.Cosmos.EventStore/Arguments.cs | 76 ++- src/Atc.Cosmos.EventStore/Checkpoint.cs | 29 +- .../Checkpoint{TState}.cs | 37 +- src/Atc.Cosmos.EventStore/ConsumerGroup.cs | 35 +- .../EventDataConverterPipelineBuilder.cs | 3 - .../Converters/EventDocumentConverter.cs | 3 - .../Converters/EventNameConverter.cs | 24 +- .../Converters/FaultedEventDataConverter.cs | 1 - .../Converters/IEventDataConverter.cs | 1 - .../Converters/NamedEventConverter.cs | 1 - .../Converters/StreamIdConverter.cs | 24 +- .../Converters/StreamVersionConverter.cs | 20 +- .../Converters/TimeSpanConverter.cs | 26 +- .../Converters/UnknownEventDataConverter.cs | 1 - .../Cosmos/CheckpointDocument.cs | 18 +- .../Cosmos/CheckpointDocument{TState}.cs | 18 +- .../Cosmos/CosmosBatchWriter.cs | 149 +++--- .../Cosmos/CosmosCheckpointReader.cs | 65 ++- .../Cosmos/CosmosCheckpointWriter.cs | 63 ++- .../Cosmos/CosmosClientFactory.cs | 74 ++- .../Cosmos/CosmosContainerProvider.cs | 73 ++- .../Cosmos/CosmosEventSerializer.cs | 140 +++--- .../Cosmos/CosmosEventStoreInitializer.cs | 224 +++++---- .../Cosmos/CosmosMetadataReader.cs | 77 ++- .../Cosmos/CosmosStreamIndexReader.cs | 102 ++-- .../Cosmos/CosmosStreamIterator.cs | 56 ++- .../Cosmos/CosmosStreamQueryBuilder.cs | 81 ++-- .../Cosmos/CosmosSubscriptionFactory.cs | 91 ++-- .../Cosmos/CosmosSubscriptionProcessor.cs | 64 ++- .../Cosmos/CosmosSubscriptionRemover.cs | 69 ++- .../Cosmos/GuidEventIdProvider.cs | 12 +- .../Cosmos/ICosmosClientFactory.cs | 9 +- .../Cosmos/IEventStoreContainerProvider.cs | 13 +- .../Cosmos/IEventStoreInitializer.cs | 2 - .../EventCatalogBuilder.cs | 25 +- .../EventStoreOptionsBuilder.cs | 132 +++--- .../IEventCatalogBuilder.cs | 9 +- .../ServiceCollectionExtensions.cs | 44 +- .../Diagnostics/EventStoreDiagnostics.cs | 15 +- .../Diagnostics/ISubscriptionActivity.cs | 9 +- .../Diagnostics/ISubscriptionTelemetry.cs | 11 +- .../Diagnostics/SubscriptionActivity.cs | 43 +- .../Diagnostics/SubscriptionTelemetry.cs | 78 ++-- src/Atc.Cosmos.EventStore/EventName.cs | 59 ++- .../EventNotRegisteredException.cs | 24 +- src/Atc.Cosmos.EventStore/EventStoreClient.cs | 245 +++++----- .../EventStoreClientOptions.cs | 157 +++---- .../EventStoreException.cs | 28 +- .../EventStoreManagementClient.cs | 39 +- .../Events/EventBatchProducer.cs | 3 - .../Events/EventCatalog.cs | 59 ++- .../Events/EventDocument{T}.cs | 39 +- .../Events/EventMetadata.cs | 32 +- .../Events/EventMetadataNames.cs | 17 +- .../Events/IDateTimeProvider.cs | 9 +- .../Events/IEventCatalog.cs | 7 +- .../Events/IEventIdProvider.cs | 9 +- .../Events/IEventNameProvider.cs | 9 +- .../Events/IEventTypeProvider.cs | 2 - src/Atc.Cosmos.EventStore/FaultedEvent.cs | 2 - src/Atc.Cosmos.EventStore/IEvent.cs | 23 +- src/Atc.Cosmos.EventStore/IEventMetadata.cs | 67 ++- .../IEventStoreClient.cs | 252 +++++----- .../IEventStoreManagementClient.cs | 96 ++-- src/Atc.Cosmos.EventStore/IStreamIndex.cs | 19 +- src/Atc.Cosmos.EventStore/IStreamMetadata.cs | 17 +- .../IStreamSubscription.cs | 27 +- .../InMemory/InMemoryStore.cs | 143 +++--- .../InMemory/InMemoryStoreInitializer.cs | 19 +- src/Atc.Cosmos.EventStore/IsExternalInit.cs | 21 +- .../ProcessEventsHandler.cs | 25 +- .../ProcessExceptionHandler.cs | 24 +- .../StreamClosedException.cs | 42 +- .../StreamConflictReason.cs | 31 +- src/Atc.Cosmos.EventStore/StreamId.cs | 60 ++- src/Atc.Cosmos.EventStore/StreamReadFilter.cs | 23 +- src/Atc.Cosmos.EventStore/StreamResponse.cs | 15 +- src/Atc.Cosmos.EventStore/StreamState.cs | 31 +- src/Atc.Cosmos.EventStore/StreamVersion.cs | 186 ++++---- .../StreamVersionConflictException.cs | 43 +- .../StreamWriteConflictException.cs | 50 +- .../StreamWriteOptions.cs | 23 +- .../Streams/IEventBatchProducer.cs | 15 +- .../Streams/IStreamBatchWriter.cs | 14 +- .../Streams/IStreamCheckpointReader.cs | 16 +- .../Streams/IStreamCheckpointWriter.cs | 20 +- .../Streams/IStreamIndexReader.cs | 17 +- .../Streams/IStreamInfoReader.cs | 14 +- .../Streams/IStreamIterator.cs | 18 +- .../Streams/IStreamMetadataReader.cs | 10 +- .../Streams/IStreamReadValidator.cs | 9 +- .../Streams/IStreamReader.cs | 18 +- .../Streams/IStreamSubscriptionFactory.cs | 17 +- .../Streams/IStreamSubscriptionRemover.cs | 14 +- .../Streams/IStreamValidator.cs | 9 +- .../Streams/IStreamWriteValidator.cs | 9 +- .../Streams/IStreamWriter.cs | 21 +- .../Streams/StreamBatch.cs | 24 +- .../Streams/StreamIndex.cs | 20 +- .../Streams/StreamInfoReader.cs | 24 +- .../Streams/StreamMetadata.cs | 60 ++- .../Streams/StreamReadValidator.cs | 26 +- .../Streams/StreamReader.cs | 76 ++- .../Streams/StreamWriteValidator.cs | 28 +- .../Streams/StreamWriter.cs | 81 ++-- .../Validators/StreamClosedValidator.cs | 13 +- .../Validators/StreamEmptyValidator.cs | 39 +- .../StreamExpectedVersionValidator.cs | 39 +- .../Validators/StreamNotEmptyValidator.cs | 39 +- .../SubscriptionStartOptions.cs | 35 +- .../UtcDateTimeProvider.cs | 12 +- .../Internal/ProjectionBuilderTests.cs | 88 ++-- .../Mocks/TestProjection.cs | 33 +- .../TestProjectionMissingFilterAttribute.cs | 31 +- .../Projections/ProjectionFilterTests.cs | 139 +++--- .../SampleEvent.cs | 9 +- .../SampleEventTypeProvider.cs | 10 +- .../EventDataConverterPipelineBuilderTests.cs | 1 - .../FaultedEventDataConverterTests.cs | 73 ++- .../Converters/NamedEventConverterTests.cs | 86 ++-- .../UnknownEventDataConverterTests.cs | 65 ++- .../Cosmos/CosmosBatchWriterTests.cs | 280 ++++++----- .../Cosmos/CosmosCheckpointReaderTests.cs | 263 ++++++----- .../Cosmos/CosmosCheckpointWriterTests.cs | 208 ++++----- .../Cosmos/CosmosContainerProviderTests.cs | 159 ++++--- .../Cosmos/CosmosMetadataReaderTests.cs | 408 ++++++++-------- .../Cosmos/CosmosStreamIteratorTests.cs | 412 ++++++++--------- .../EventStoreClientOptionsTests.cs | 92 ++-- .../EventStoreClientTests.cs | 401 ++++++++-------- .../Events/EventBatchProducerTests.cs | 437 +++++++++--------- .../Events/EventCatalogTests.cs | 80 ++-- .../Fakes/EventOne.cs | 2 - .../Fakes/FakeEventDataConverter.cs | 3 +- .../StreamIdTests.cs | 117 +++-- .../StreamVersionTests.cs | 346 +++++++------- .../Streams/StreamInfoReaderTests.cs | 107 +++-- .../Streams/StreamReadValidatorTests.cs | 164 ++++--- .../Streams/StreamReaderTests.cs | 300 ++++++------ .../Streams/StreamWriteValidatorTests.cs | 104 ++--- .../Streams/StreamWriterTests.cs | 200 ++++---- .../Validators/StreamClosedValidatorTests.cs | 87 ++-- .../Validators/StreamEmptyValidatorTests.cs | 93 ++-- .../StreamExpectedVersionValidatorTests.cs | 93 ++-- .../StreamNotEmptyValidatorValidatorTests.cs | 99 ++-- 209 files changed, 5989 insertions(+), 6550 deletions(-) diff --git a/src/Atc.Cosmos.EventStore.Cqrs/CommandBase.cs b/src/Atc.Cosmos.EventStore.Cqrs/CommandBase.cs index f026b56..45d26e4 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/CommandBase.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/CommandBase.cs @@ -1,23 +1,21 @@ -using System; using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public abstract record CommandBase( + TStreamId StreamId, + string? CommandId = default, + string? CorrelationId = default, + EventStreamVersion? RequiredVersion = default, + OnConflict Behavior = OnConflict.Fail, + int BehaviorCount = 3) : ICommand + where TStreamId : EventStreamId { - public abstract record CommandBase( - TStreamId StreamId, - string? CommandId = default, - string? CorrelationId = default, - EventStreamVersion? RequiredVersion = default, - OnConflict Behavior = OnConflict.Fail, - int BehaviorCount = 3) : ICommand - where TStreamId : EventStreamId - { - public string CommandId { get; init; } = CommandId ?? Guid.NewGuid().ToString(); + public string CommandId { get; init; } = CommandId ?? Guid.NewGuid().ToString(); - [SuppressMessage( - "Design", - "CA1033:Interface methods should be callable by child types", - Justification = "By Design")] - EventStreamId ICommand.GetEventStreamId() => StreamId; - } + [SuppressMessage( + "Design", + "CA1033:Interface methods should be callable by child types", + Justification = "By Design")] + EventStreamId ICommand.GetEventStreamId() => StreamId; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/CommandContextExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/CommandContextExtensions.cs index caaf4ee..e7811e4 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/CommandContextExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/CommandContextExtensions.cs @@ -1,29 +1,25 @@ -using System; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public static class CommandContextExtensions { - public static class CommandContextExtensions - { - public static ICommandContext AddEventWhen( - this ICommandContext context, - Func condition, - Func eventProvider) - => condition() - ? context.OnAddEvent(eventProvider()) - : context; + public static ICommandContext AddEventWhen( + this ICommandContext context, + Func condition, + Func eventProvider) + => condition() + ? context.OnAddEvent(eventProvider()) + : context; - public static ValueTask AsAsync( - this ICommandContext context) - => default; // default is a completed value task. + public static ValueTask AsAsync( + this ICommandContext context) + => default; // default is a completed value task. - private static ICommandContext OnAddEvent( - this ICommandContext context, - object @event) - { - context.AddEvent(@event); + private static ICommandContext OnAddEvent( + this ICommandContext context, + object @event) + { + context.AddEvent(@event); - return context; - } + return context; } } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/CommandResult.cs b/src/Atc.Cosmos.EventStore.Cqrs/CommandResult.cs index 39d2394..52276c5 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/CommandResult.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/CommandResult.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs -{ - public record CommandResult( - EventStreamId Id, - StreamVersion Version, - ResultType Result, - object? Response = default); -} \ No newline at end of file +namespace Atc.Cosmos.EventStore.Cqrs; + +public record CommandResult( + EventStreamId Id, + StreamVersion Version, + ResultType Result, + object? Response = default); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/CommandResultExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/CommandResultExtensions.cs index 5c2cdd9..cb82693 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/CommandResultExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/CommandResultExtensions.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public static class CommandResultExtensions { - public static class CommandResultExtensions - { - public static T GetResponse(this CommandResult result) - => (T)result.Response!; - } + public static T GetResponse(this CommandResult result) + => (T)result.Response!; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandContext.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandContext.cs index e1ff57d..0a0c186 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandContext.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandContext.cs @@ -1,18 +1,16 @@ -using System.Collections.Generic; using Atc.Cosmos.EventStore.Cqrs.Testing; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal class CommandContext : ICommandContext, ICommandContextInspector { - internal class CommandContext : ICommandContext, ICommandContextInspector - { - private readonly List appliedEvents = new(); + private readonly List appliedEvents = new(); - public IReadOnlyCollection Events - => appliedEvents; + public IReadOnlyCollection Events + => appliedEvents; - public void AddEvent(object evt) - => appliedEvents.Add(evt); + public void AddEvent(object evt) + => appliedEvents.Add(evt); - public object? ResponseObject { get; set; } - } + public object? ResponseObject { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerMetadata.cs index 1611a5c..ed1ad29 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerMetadata.cs @@ -1,24 +1,21 @@ -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs.Internal; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal class CommandHandlerMetadata : + ConsumeEventMetadata, + ICommandHandlerMetadata + where TCommand : ICommand + where THandler : ICommandHandler { - internal class CommandHandlerMetadata : - ConsumeEventMetadata, - ICommandHandlerMetadata - where TCommand : ICommand - where THandler : ICommandHandler + public CommandHandlerMetadata() + : base(typeof(THandler)) { - public CommandHandlerMetadata() - : base(typeof(THandler)) - { - } - - public ValueTask ConsumeAsync( - IEvent evt, - ICommandHandler handler, - CancellationToken cancellationToken) - => base.ConsumeAsync(evt, handler, cancellationToken); } + + public ValueTask ConsumeAsync( + IEvent evt, + ICommandHandler handler, + CancellationToken cancellationToken) + => base.ConsumeAsync(evt, handler, cancellationToken); } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs index b82bffc..32e05db 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs @@ -1,103 +1,99 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +internal class CommandProcessor : ICommandProcessor + where TCommand : ICommand { - internal class CommandProcessor : ICommandProcessor - where TCommand : ICommand - { - private readonly IStateWriter stateWriter; - private readonly IStateProjector stateProjector; - private readonly ICommandHandler handler; - private int reruns; + private readonly IStateWriter stateWriter; + private readonly IStateProjector stateProjector; + private readonly ICommandHandler handler; + private int reruns; - public CommandProcessor( - IStateWriter stateWriter, - IStateProjector stateProjector, - ICommandHandler handler) - { - this.stateWriter = stateWriter; - this.stateProjector = stateProjector; - this.handler = handler; - } + public CommandProcessor( + IStateWriter stateWriter, + IStateProjector stateProjector, + ICommandHandler handler) + { + this.stateWriter = stateWriter; + this.stateProjector = stateProjector; + this.handler = handler; + } - public async ValueTask ExecuteAsync( - TCommand command, - CancellationToken cancellationToken) + public async ValueTask ExecuteAsync( + TCommand command, + CancellationToken cancellationToken) + { + try { - try - { - reruns = GetReruns(command); - - // Read and project events to aggregate (command handler). - var state = await stateProjector - .ProjectAsync(command, handler, cancellationToken) - .ConfigureAwait(false); - - // Execute command on aggregate. - var context = new CommandContext(); - await handler - .ExecuteAsync(command, context, cancellationToken) - .ConfigureAwait(false); + reruns = GetReruns(command); - if (context.Events.Count == 0) - { - // Command did not yield any events - return new CommandResult( - state.Id, - state.Version, - ResultType.NotModified, - context.ResponseObject); - } + // Read and project events to aggregate (command handler). + var state = await stateProjector + .ProjectAsync(command, handler, cancellationToken) + .ConfigureAwait(false); - // Write context to stream. - var result = await stateWriter - .WriteEventAsync(command, context.Events, cancellationToken) - .ConfigureAwait(false); + // Execute command on aggregate. + var context = new CommandContext(); + await handler + .ExecuteAsync(command, context, cancellationToken) + .ConfigureAwait(false); + if (context.Events.Count == 0) + { + // Command did not yield any events return new CommandResult( - result.Id, - result.Version, - ResultType.Changed, + state.Id, + state.Version, + ResultType.NotModified, context.ResponseObject); } - catch (StreamWriteConflictException conflict) - { - if (ShouldRerunCommand()) - { - return await - ExecuteAsync(command, cancellationToken) - .ConfigureAwait(false); - } - return new CommandResult( - conflict.StreamId, - conflict.Version, - ResultType.Conflict); - } - catch (StreamVersionConflictException versionConflict) + // Write context to stream. + var result = await stateWriter + .WriteEventAsync(command, context.Events, cancellationToken) + .ConfigureAwait(false); + + return new CommandResult( + result.Id, + result.Version, + ResultType.Changed, + context.ResponseObject); + } + catch (StreamWriteConflictException conflict) + { + if (ShouldRerunCommand()) { - return new CommandResult( - versionConflict.StreamId, - versionConflict.Version, - GetResultType(versionConflict)); + return await + ExecuteAsync(command, cancellationToken) + .ConfigureAwait(false); } + + return new CommandResult( + conflict.StreamId, + conflict.Version, + ResultType.Conflict); } + catch (StreamVersionConflictException versionConflict) + { + return new CommandResult( + versionConflict.StreamId, + versionConflict.Version, + GetResultType(versionConflict)); + } + } - private static int GetReruns(TCommand command) - => command.Behavior == OnConflict.RerunCommand - ? command.BehaviorCount - : 0; + private static int GetReruns(TCommand command) + => command.Behavior == OnConflict.RerunCommand + ? command.BehaviorCount + : 0; - private static ResultType GetResultType(StreamVersionConflictException versionConflict) - => versionConflict.Reason switch - { - StreamConflictReason.StreamIsEmpty => ResultType.NotFound, - StreamConflictReason.StreamIsNotEmpty => ResultType.Exists, - _ => ResultType.Conflict, - }; + private static ResultType GetResultType(StreamVersionConflictException versionConflict) + => versionConflict.Reason switch + { + StreamConflictReason.StreamIsEmpty => ResultType.NotFound, + StreamConflictReason.StreamIsNotEmpty => ResultType.Exists, + _ => ResultType.Conflict, + }; - private bool ShouldRerunCommand() - => reruns-- > 0; - } + private bool ShouldRerunCommand() + => reruns-- > 0; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs index 2711903..f8596b2 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs @@ -1,20 +1,18 @@ -using System; using Microsoft.Extensions.DependencyInjection; -namespace Atc.Cosmos.EventStore.Cqrs.Commands -{ - internal class CommandProcessorFactory : ICommandProcessorFactory - { - private readonly IServiceProvider provider; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; - public CommandProcessorFactory( - IServiceProvider provider) - { - this.provider = provider; - } +internal class CommandProcessorFactory : ICommandProcessorFactory +{ + private readonly IServiceProvider provider; - public ICommandProcessor Create() - where TCommand : ICommand - => provider.GetRequiredService>(); + public CommandProcessorFactory( + IServiceProvider provider) + { + this.provider = provider; } + + public ICommandProcessor Create() + where TCommand : ICommand + => provider.GetRequiredService>(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/ICommandHandlerMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/ICommandHandlerMetadata.cs index 09695d5..6ffdc4c 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/ICommandHandlerMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/ICommandHandlerMetadata.cs @@ -1,18 +1,14 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +internal interface ICommandHandlerMetadata + where TCommand : ICommand { - internal interface ICommandHandlerMetadata - where TCommand : ICommand - { - bool CanConsumeEvent(IEvent evt); + bool CanConsumeEvent(IEvent evt); - bool IsNotConsumingEvents(); + bool IsNotConsumingEvents(); - ValueTask ConsumeAsync( - IEvent evt, - ICommandHandler handler, - CancellationToken cancellationToken); - } + ValueTask ConsumeAsync( + IEvent evt, + ICommandHandler handler, + CancellationToken cancellationToken); } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateProjector.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateProjector.cs index 8e477a7..7744dc7 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateProjector.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateProjector.cs @@ -1,14 +1,10 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +internal interface IStateProjector + where TCommand : ICommand { - internal interface IStateProjector - where TCommand : ICommand - { - ValueTask ProjectAsync( - TCommand command, - ICommandHandler handler, - CancellationToken cancellationToken); - } + ValueTask ProjectAsync( + TCommand command, + ICommandHandler handler, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateWriter.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateWriter.cs index c527acd..53d7836 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateWriter.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStateWriter.cs @@ -1,15 +1,10 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +internal interface IStateWriter + where TCommand : ICommand { - internal interface IStateWriter - where TCommand : ICommand - { - ValueTask WriteEventAsync( - TCommand command, - IReadOnlyCollection events, - CancellationToken cancellationToken); - } + ValueTask WriteEventAsync( + TCommand command, + IReadOnlyCollection events, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStreamState.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStreamState.cs index a469986..c5b3f52 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStreamState.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/IStreamState.cs @@ -1,9 +1,8 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Commands +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal interface IStreamState { - internal interface IStreamState - { - StreamId Id { get; } + StreamId Id { get; } - StreamVersion Version { get; } - } + StreamVersion Version { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs index 179623d..709d379 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs @@ -1,72 +1,69 @@ -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal class StateProjector : IStateProjector + where TCommand : ICommand { - internal class StateProjector : IStateProjector - where TCommand : ICommand + private readonly IEventStoreClient eventStore; + private readonly IStreamReadValidator readValidator; + private readonly ICommandHandlerMetadata handlerMetadata; + + public StateProjector( + IEventStoreClient eventStore, + IStreamReadValidator readValidator, + ICommandHandlerMetadata handlerMetadata) { - private readonly IEventStoreClient eventStore; - private readonly IStreamReadValidator readValidator; - private readonly ICommandHandlerMetadata handlerMetadata; + this.eventStore = eventStore; + this.readValidator = readValidator; + this.handlerMetadata = handlerMetadata; + } - public StateProjector( - IEventStoreClient eventStore, - IStreamReadValidator readValidator, - ICommandHandlerMetadata handlerMetadata) + public async ValueTask ProjectAsync( + TCommand command, + ICommandHandler handler, + CancellationToken cancellationToken) + { + var state = new StreamState { - this.eventStore = eventStore; - this.readValidator = readValidator; - this.handlerMetadata = handlerMetadata; - } + Id = command.GetEventStreamId().Value, + Version = 0L, + }; - public async ValueTask ProjectAsync( - TCommand command, - ICommandHandler handler, - CancellationToken cancellationToken) + if (handlerMetadata.IsNotConsumingEvents()) { - var state = new StreamState - { - Id = command.GetEventStreamId().Value, - Version = 0L, - }; - - if (handlerMetadata.IsNotConsumingEvents()) - { - var metadata = await eventStore - .GetStreamInfoAsync( - state.Id, - cancellationToken) - .ConfigureAwait(false); - - readValidator.Validate( - metadata, - (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any); + var metadata = await eventStore + .GetStreamInfoAsync( + state.Id, + cancellationToken) + .ConfigureAwait(false); - state.Version = metadata.Version; + readValidator.Validate( + metadata, + (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any); - return state; - } + state.Version = metadata.Version; - await foreach (var evt in eventStore - .ReadFromStreamAsync( - state.Id, - (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any, - cancellationToken: cancellationToken) - .ConfigureAwait(false)) - { - await handlerMetadata - .ConsumeAsync( - evt, - handler, - cancellationToken) - .ConfigureAwait(false); + return state; + } - state.Version = evt.Metadata.Version; - } + await foreach (var evt in eventStore + .ReadFromStreamAsync( + state.Id, + (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any, + cancellationToken: cancellationToken) + .ConfigureAwait(false)) + { + await handlerMetadata + .ConsumeAsync( + evt, + handler, + cancellationToken) + .ConfigureAwait(false); - return state; + state.Version = evt.Metadata.Version; } + + return state; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateWriter.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateWriter.cs index 6893ae5..b6fb301 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateWriter.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateWriter.cs @@ -1,84 +1,79 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Commands; -namespace Atc.Cosmos.EventStore.Cqrs.Commands +internal class StateWriter : IStateWriter + where TCommand : ICommand { - internal class StateWriter : IStateWriter - where TCommand : ICommand + private readonly IEventStoreClient eventStore; + + public StateWriter( + IEventStoreClient eventStore) { - private readonly IEventStoreClient eventStore; + this.eventStore = eventStore; + } - public StateWriter( - IEventStoreClient eventStore) + public ValueTask WriteEventAsync( + TCommand command, + IReadOnlyCollection events, + CancellationToken cancellationToken) + { + var id = command.GetEventStreamId(); + var version = (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any; + var streamOptions = new StreamWriteOptions { - this.eventStore = eventStore; - } + CausationId = command.CommandId, + CorrelationId = command.CorrelationId, + }; - public ValueTask WriteEventAsync( - TCommand command, - IReadOnlyCollection events, - CancellationToken cancellationToken) - { - var id = command.GetEventStreamId(); - var version = (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any; - var streamOptions = new StreamWriteOptions - { - CausationId = command.CommandId, - CorrelationId = command.CorrelationId, - }; + return WriteToEventStoreAsync( + id.Value, + version, + streamOptions, + events, + GetRetries(command), + cancellationToken); + } - return WriteToEventStoreAsync( - id.Value, - version, - streamOptions, - events, - GetRetries(command), - cancellationToken); - } + private static int GetRetries(TCommand command) + => command.Behavior == OnConflict.Retry + ? command.BehaviorCount + : 0; - private static int GetRetries(TCommand command) - => command.Behavior == OnConflict.Retry - ? command.BehaviorCount - : 0; + private async ValueTask WriteToEventStoreAsync( + StreamId id, + StreamVersion version, + StreamWriteOptions options, + IReadOnlyCollection events, + int reties, + CancellationToken cancellationToken) + { + try + { + var response = await eventStore + .WriteToStreamAsync( + id, + events, + version, + options, + cancellationToken) + .ConfigureAwait(false); - private async ValueTask WriteToEventStoreAsync( - StreamId id, - StreamVersion version, - StreamWriteOptions options, - IReadOnlyCollection events, - int reties, - CancellationToken cancellationToken) + return new CommandResult(id, response.Version, ResultType.Changed); + } + catch (StreamWriteConflictException) { - try + if (reties > 0) { - var response = await eventStore - .WriteToStreamAsync( + return await WriteToEventStoreAsync( id, - events, version, options, + events, + reties - 1, cancellationToken) .ConfigureAwait(false); - - return new CommandResult(id, response.Version, ResultType.Changed); } - catch (StreamWriteConflictException) - { - if (reties > 0) - { - return await WriteToEventStoreAsync( - id, - version, - options, - events, - reties - 1, - cancellationToken) - .ConfigureAwait(false); - } - throw; - } + throw; } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StreamState.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StreamState.cs index 125842c..5621179 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StreamState.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StreamState.cs @@ -1,9 +1,8 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Commands +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal class StreamState : IStreamState { - internal class StreamState : IStreamState - { - public StreamId Id { get; set; } + public StreamId Id { get; set; } - public StreamVersion Version { get; set; } - } + public StreamVersion Version { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventCatalogBuilderExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventCatalogBuilderExtensions.cs index 2efa9bc..c9eb946 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventCatalogBuilderExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventCatalogBuilderExtensions.cs @@ -1,91 +1,88 @@ -using System; -using System.Linq; using System.Reflection; using Atc.Cosmos.EventStore.Cqrs; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class EventCatalogBuilderExtensions { - public static class EventCatalogBuilderExtensions + /// + /// Search assembly for events and register them in catalog. + /// + /// Only classes marked with will be added to the catalog. + /// Use this type to identify the assembly to scan. + /// Catalog build to add events to. + /// Builder. + public static IEventCatalogBuilder FromAssembly(this IEventCatalogBuilder builder) { - /// - /// Search assembly for events and register them in catalog. - /// - /// Only classes marked with will be added to the catalog. - /// Use this type to identify the assembly to scan. - /// Catalog build to add events to. - /// Builder. - public static IEventCatalogBuilder FromAssembly(this IEventCatalogBuilder builder) + var types = typeof(T) + .Assembly + .GetTypes() + .Where(t => t.GetCustomAttribute() != null) + .Select(t => new { Type = t, Name = t.GetCustomAttribute()!.Name.Value }) + .ToArray(); + + foreach (var type in types) { - var types = typeof(T) - .Assembly - .GetTypes() - .Where(t => t.GetCustomAttribute() != null) - .Select(t => new { Type = t, Name = t.GetCustomAttribute()!.Name.Value }) - .ToArray(); + builder.FromType(type.Name, type.Type); + } - foreach (var type in types) - { - builder.FromType(type.Name, type.Type); - } + return builder; + } + /// + /// Search namespace for events and register them in catalog. + /// + /// Only classes marked with will be added to the catalog. + /// Use this types namespace. + /// Catalog build to add events to. + /// Builder. + public static IEventCatalogBuilder FromNamespace(this IEventCatalogBuilder builder) + { + var ns = typeof(T) + .Namespace; + if (ns is null) + { return builder; } - /// - /// Search namespace for events and register them in catalog. - /// - /// Only classes marked with will be added to the catalog. - /// Use this types namespace. - /// Catalog build to add events to. - /// Builder. - public static IEventCatalogBuilder FromNamespace(this IEventCatalogBuilder builder) - { - var ns = typeof(T) - .Namespace; - if (ns is null) - { - return builder; - } + var types = typeof(T) + .Assembly + .GetTypes() + .Where(t => ns.Equals(t.Namespace, StringComparison.Ordinal)) + .Where(t => t.GetCustomAttribute() != null) + .Select(t => new { Type = t, Name = t.GetCustomAttribute()!.Name.Value }) + .ToArray(); - var types = typeof(T) - .Assembly - .GetTypes() - .Where(t => ns.Equals(t.Namespace, StringComparison.Ordinal)) - .Where(t => t.GetCustomAttribute() != null) - .Select(t => new { Type = t, Name = t.GetCustomAttribute()!.Name.Value }) - .ToArray(); + foreach (var type in types) + { + builder.FromType(type.Name, type.Type); + } - foreach (var type in types) - { - builder.FromType(type.Name, type.Type); - } + return builder; + } - return builder; - } + /// + /// Add event to catalog. + /// + /// Type to add. + /// Catalog to add type to. + /// Builder. + /// If type is not decorated with . + public static IEventCatalogBuilder FromType(this IEventCatalogBuilder builder) + { + var name = typeof(T) + .GetCustomAttribute() + ?.Name.Value; - /// - /// Add event to catalog. - /// - /// Type to add. - /// Catalog to add type to. - /// Builder. - /// If type is not decorated with . - public static IEventCatalogBuilder FromType(this IEventCatalogBuilder builder) + if (name is null) { - var name = typeof(T) - .GetCustomAttribute() - ?.Name.Value; - - if (name is null) - { - throw new ArgumentException( - $"Type does not have {nameof(StreamEventAttribute)} declared on it.", - typeof(T).Name); - } + throw new ArgumentException( + $"Type does not have {nameof(StreamEventAttribute)} declared on it.", + typeof(T).Name); + } - builder.FromType(name, typeof(T)); + builder.FromType(name, typeof(T)); - return builder; - } + return builder; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs index f114f0c..6df543a 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs @@ -1,4 +1,3 @@ -using System; using Atc.Cosmos.EventStore.Cqrs; using Atc.Cosmos.EventStore.Cqrs.Commands; using Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal; @@ -7,31 +6,30 @@ using Atc.Cosmos.EventStore.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class EventStoreOptionsBuilderExtensions { - public static class EventStoreOptionsBuilderExtensions + public static EventStoreOptionsBuilder UseCQRS( + this EventStoreOptionsBuilder builder, + Action configure) { - public static EventStoreOptionsBuilder UseCQRS( - this EventStoreOptionsBuilder builder, - Action configure) - { - var cqrsBuilder = new EventStoreCqrsBuilder(builder); + var cqrsBuilder = new EventStoreCqrsBuilder(builder); - configure?.Invoke(cqrsBuilder); + configure?.Invoke(cqrsBuilder); - builder.Services.AddSingleton(typeof(IStateProjector<>), typeof(StateProjector<>)); - builder.Services.AddSingleton(typeof(IStateWriter<>), typeof(StateWriter<>)); - builder.Services.AddTransient(typeof(ICommandProcessor<>), typeof(CommandProcessor<>)); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(typeof(IStateProjector<>), typeof(StateProjector<>)); + builder.Services.AddSingleton(typeof(IStateWriter<>), typeof(StateWriter<>)); + builder.Services.AddTransient(typeof(ICommandProcessor<>), typeof(CommandProcessor<>)); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); - builder.Services.AddSingleton(typeof(ProjectionMetadata<>)); + builder.Services.AddSingleton(typeof(ProjectionMetadata<>)); - builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); - return builder; - } + return builder; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IEventStoreCqrsBuilder.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IEventStoreCqrsBuilder.cs index b7c77dd..3c53b15 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IEventStoreCqrsBuilder.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IEventStoreCqrsBuilder.cs @@ -1,24 +1,21 @@ -using System; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public interface IEventStoreCqrsBuilder { - public interface IEventStoreCqrsBuilder - { - IEventStoreCqrsBuilder AddInitialization( - int throughput, - Func? additionInitialization = default); + IEventStoreCqrsBuilder AddInitialization( + int throughput, + Func? additionInitialization = default); - IEventStoreCqrsBuilder AddCommandsFromAssembly(); + IEventStoreCqrsBuilder AddCommandsFromAssembly(); - IEventStoreCqrsBuilder AddCommand() - where TCommandHandler : class, ICommandHandler - where TCommand : ICommand; + IEventStoreCqrsBuilder AddCommand() + where TCommandHandler : class, ICommandHandler + where TCommand : ICommand; - IEventStoreCqrsBuilder AddProjectionJob( - string name, - Action? configure = default) - where TProjection : class, IProjection; - } + IEventStoreCqrsBuilder AddProjectionJob( + string name, + Action? configure = default) + where TProjection : class, IProjection; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IProjectionBuilder.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IProjectionBuilder.cs index 78724b4..a3507f2 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IProjectionBuilder.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/IProjectionBuilder.cs @@ -1,18 +1,17 @@ using Atc.Cosmos.EventStore; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public interface IProjectionBuilder { - public interface IProjectionBuilder - { - /// - /// Filter on stream id for events projected. - /// - /// Filter pattern. - /// The builder. - IProjectionBuilder WithFilter(string filter); + /// + /// Filter on stream id for events projected. + /// + /// Filter pattern. + /// The builder. + IProjectionBuilder WithFilter(string filter); - IProjectionBuilder WithJobName(string name); + IProjectionBuilder WithJobName(string name); - IProjectionBuilder WithExceptionHandler(ProcessExceptionHandler handler); - } + IProjectionBuilder WithExceptionHandler(ProcessExceptionHandler handler); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/EventStoreCqrsBuilder.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/EventStoreCqrsBuilder.cs index 2f6725d..51830df 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/EventStoreCqrsBuilder.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/EventStoreCqrsBuilder.cs @@ -1,8 +1,4 @@ -using System; -using System.Linq; using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Cqrs.Commands; using Atc.Cosmos.EventStore.Cqrs.Internal; @@ -12,103 +8,102 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal +namespace Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal; + +internal class EventStoreCqrsBuilder : IEventStoreCqrsBuilder { - internal class EventStoreCqrsBuilder : IEventStoreCqrsBuilder - { - private readonly EventStoreOptionsBuilder builder; + private readonly EventStoreOptionsBuilder builder; - public EventStoreCqrsBuilder( - EventStoreOptionsBuilder builder) - => this.builder = builder; + public EventStoreCqrsBuilder( + EventStoreOptionsBuilder builder) + => this.builder = builder; - public IEventStoreCqrsBuilder AddCommandsFromAssembly() + public IEventStoreCqrsBuilder AddCommandsFromAssembly() + { + var commands = typeof(TAssembly) + .Assembly + .GetTypes() + .Where(t => !t.IsAbstract) + .SelectMany(t => t + .GetInterfaces() + .Where(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(ICommandHandler<>))) + .Select(i => new + { + CommandHandlerType = t, + CommandType = i.GetGenericArguments()[0], + })) + .Select(t => typeof(EventStoreCqrsBuilder) + .GetRuntimeMethods() + .First(m => m.Name.Equals(nameof(AddCommand), StringComparison.OrdinalIgnoreCase)) + .MakeGenericMethod(t.CommandType, t.CommandHandlerType)) + .ToArray(); + + foreach (var cmd in commands) { - var commands = typeof(TAssembly) - .Assembly - .GetTypes() - .Where(t => !t.IsAbstract) - .SelectMany(t => t - .GetInterfaces() - .Where(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(ICommandHandler<>))) - .Select(i => new - { - CommandHandlerType = t, - CommandType = i.GetGenericArguments()[0], - })) - .Select(t => typeof(EventStoreCqrsBuilder) - .GetRuntimeMethods() - .First(m => m.Name.Equals(nameof(AddCommand), StringComparison.OrdinalIgnoreCase)) - .MakeGenericMethod(t.CommandType, t.CommandHandlerType)) - .ToArray(); - - foreach (var cmd in commands) - { - cmd.Invoke(this, Array.Empty()); - } - - return this; + cmd.Invoke(this, Array.Empty()); } - public IEventStoreCqrsBuilder AddCommand() - where TCommandHandler : class, ICommandHandler - where TCommand : ICommand - { - builder.Services.AddTransient, TCommandHandler>(); - builder.Services.AddSingleton, CommandHandlerMetadata>(); + return this; + } - return this; - } + public IEventStoreCqrsBuilder AddCommand() + where TCommandHandler : class, ICommandHandler + where TCommand : ICommand + { + builder.Services.AddTransient, TCommandHandler>(); + builder.Services.AddSingleton, CommandHandlerMetadata>(); - public IEventStoreCqrsBuilder AddProjectionJob( - string name, - Action? configure = default) - where TProjection : class, IProjection - { - builder.Services.AddHostedService>(); - builder.Services.AddSingleton, ProjectionProcessor>(); - builder.Services.AddTransient(); - builder.Services.TryAddSingleton( - new DependencyInitializer(() => Task.CompletedTask)); + return this; + } - var projectionBuilder = new ProjectionBuilder(name); + public IEventStoreCqrsBuilder AddProjectionJob( + string name, + Action? configure = default) + where TProjection : class, IProjection + { + builder.Services.AddHostedService>(); + builder.Services.AddSingleton, ProjectionProcessor>(); + builder.Services.AddTransient(); + builder.Services.TryAddSingleton( + new DependencyInitializer(() => Task.CompletedTask)); - configure?.Invoke(projectionBuilder); + var projectionBuilder = new ProjectionBuilder(name); - builder - .Services - .Configure( - typeof(TProjection).Name, - options => projectionBuilder.Build(options)); + configure?.Invoke(projectionBuilder); - return this; - } + builder + .Services + .Configure( + typeof(TProjection).Name, + options => projectionBuilder.Build(options)); - public IEventStoreCqrsBuilder AddInitialization( - int throughput, - Func? additionInitialization = null) - { - builder.Services.RemoveAll(); - builder.Services.AddSingleton(s => - new DependencyInitializer( - async () => + return this; + } + + public IEventStoreCqrsBuilder AddInitialization( + int throughput, + Func? additionInitialization = null) + { + builder.Services.RemoveAll(); + builder.Services.AddSingleton(s => + new DependencyInitializer( + async () => + { + await s.GetRequiredService() + .CreateEventStoreAsync( + ThroughputProperties.CreateManualThroughput(throughput), + CancellationToken.None) + .ConfigureAwait(false); + + if (additionInitialization is not null) { - await s.GetRequiredService() - .CreateEventStoreAsync( - ThroughputProperties.CreateManualThroughput(throughput), - CancellationToken.None) - .ConfigureAwait(false); - - if (additionInitialization is not null) - { - await additionInitialization - .Invoke(s) - .ConfigureAwait(false); - } - })); - builder.Services.AddHostedService(); - - return this; - } + await additionInitialization + .Invoke(s) + .ConfigureAwait(false); + } + })); + builder.Services.AddHostedService(); + + return this; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/ProjectionBuilder.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/ProjectionBuilder.cs index 14e4f7d..dbf562c 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/ProjectionBuilder.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/Internal/ProjectionBuilder.cs @@ -1,67 +1,63 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; using Atc.Cosmos.EventStore.Cqrs.Projections; using Microsoft.Extensions.DependencyInjection; -namespace Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal +namespace Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal; + +internal class ProjectionBuilder : IProjectionBuilder { - internal class ProjectionBuilder : IProjectionBuilder - { - private readonly List filters; - private ProcessExceptionHandler exceptionHandler; - private string name; + private readonly List filters; + private ProcessExceptionHandler exceptionHandler; + private string name; - public ProjectionBuilder(string name) - { - this.name = name; - filters = new List(); - exceptionHandler = ProjectionOptions.EmptyExceptionHandler; - } + public ProjectionBuilder(string name) + { + this.name = name; + filters = new List(); + exceptionHandler = ProjectionOptions.EmptyExceptionHandler; + } - public IProjectionBuilder WithFilter(string filter) - { - filters.Add(new ProjectionFilter(filter)); + public IProjectionBuilder WithFilter(string filter) + { + filters.Add(new ProjectionFilter(filter)); - return this; - } + return this; + } - public IProjectionBuilder WithJobName(string name) - { - this.name = name; + public IProjectionBuilder WithJobName(string name) + { + this.name = name; - return this; - } + return this; + } - public IProjectionBuilder WithExceptionHandler(ProcessExceptionHandler handler) - { - this.exceptionHandler = handler; + public IProjectionBuilder WithExceptionHandler(ProcessExceptionHandler handler) + { + this.exceptionHandler = handler; - return this; - } + return this; + } - public void Build(ProjectionOptions options) - where TProjection : class, IProjection + public void Build(ProjectionOptions options) + where TProjection : class, IProjection + { + SetFiltersFromProjection(); + if (filters.Count == 0) { - SetFiltersFromProjection(); - if (filters.Count == 0) - { - throw new ArgumentException( - $"Please provide a filter for type {typeof(TProjection)}"); - } - - options.Name = name; - options.Filters = filters; - options.ExceptionHandler = exceptionHandler; + throw new ArgumentException( + $"Please provide a filter for type {typeof(TProjection)}"); } - private void SetFiltersFromProjection() - => Array.ForEach( - typeof(T) - .GetCustomAttributes() - .Select(a => a.Filter) - .ToArray(), - f => WithFilter(f)); + options.Name = name; + options.Filters = filters; + options.ExceptionHandler = exceptionHandler; } + + private void SetFiltersFromProjection() + => Array.ForEach( + typeof(T) + .GetCustomAttributes() + .Select(a => a.Filter) + .ToArray(), + f => WithFilter(f)); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionDiagnostics.cs b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionDiagnostics.cs index ea74776..9517589 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionDiagnostics.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionDiagnostics.cs @@ -1,17 +1,13 @@ -using System.Collections.Generic; -using System.Linq; +namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics; -namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics +internal interface IProjectionDiagnostics { - internal interface IProjectionDiagnostics - { - void ProcessingGroupedEvents( - string projectionName, - IGrouping[] groupedEvents, - IEnumerable batch); + void ProcessingGroupedEvents( + string projectionName, + IGrouping[] groupedEvents, + IEnumerable batch); - IProjectionProcessOperation StartStreamProjection( - string projectionName, - StreamId key); - } + IProjectionProcessOperation StartStreamProjection( + string projectionName, + StreamId key); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionProcessOperation.cs b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionProcessOperation.cs index e71eb24..b946380 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionProcessOperation.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/IProjectionProcessOperation.cs @@ -1,18 +1,14 @@ -using System; -using System.Linq; +namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics; -namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics +internal interface IProjectionProcessOperation { - internal interface IProjectionProcessOperation - { - void ProjectionSkipped( - IGrouping events); + void ProjectionSkipped( + IGrouping events); - void ProjectionCompleted( - IGrouping events); + void ProjectionCompleted( + IGrouping events); - void ProjectionFailed( - IGrouping events, - Exception exception); - } + void ProjectionFailed( + IGrouping events, + Exception exception); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionDiagnostics.cs b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionDiagnostics.cs index 573798d..1b22055 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionDiagnostics.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionDiagnostics.cs @@ -1,26 +1,23 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; +using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics +namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics; + +[SuppressMessage( + "Performance", + "CA1812:Avoid uninstantiated internal classes", + Justification = "Class is used, rule is broken")] +internal class ProjectionDiagnostics : IProjectionDiagnostics { - [SuppressMessage( - "Performance", - "CA1812:Avoid uninstantiated internal classes", - Justification = "Class is used, rule is broken")] - internal class ProjectionDiagnostics : IProjectionDiagnostics + public void ProcessingGroupedEvents( + string projectionName, + IGrouping[] groupedEvents, + IEnumerable batch) { - public void ProcessingGroupedEvents( - string projectionName, - IGrouping[] groupedEvents, - IEnumerable batch) - { - // Ignore - } - - public IProjectionProcessOperation StartStreamProjection( - string projectionName, - StreamId key) - => new ProjectionProcessOperation(); + // Ignore } + + public IProjectionProcessOperation StartStreamProjection( + string projectionName, + StreamId key) + => new ProjectionProcessOperation(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionProcessOperation.cs b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionProcessOperation.cs index 0d0f285..193e33a 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionProcessOperation.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Diagnostics/ProjectionProcessOperation.cs @@ -1,27 +1,23 @@ -using System; -using System.Linq; +namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics; -namespace Atc.Cosmos.EventStore.Cqrs.Diagnostics +internal class ProjectionProcessOperation : IProjectionProcessOperation { - internal class ProjectionProcessOperation : IProjectionProcessOperation + public void ProjectionCompleted( + IGrouping events) { - public void ProjectionCompleted( - IGrouping events) - { - // Ignore - } + // Ignore + } - public void ProjectionFailed( - IGrouping events, - Exception exception) - { - // Ignore - } + public void ProjectionFailed( + IGrouping events, + Exception exception) + { + // Ignore + } - public void ProjectionSkipped( - IGrouping events) - { - // Ignore - } + public void ProjectionSkipped( + IGrouping events) + { + // Ignore } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs index 2e81312..b28580c 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs @@ -1,12 +1,9 @@ -using System; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs -{ - public record EventMetadata( - string EventId, - EventStreamId StreamId, - DateTimeOffset Timestamp, - long Version, - string? CorrelationId, - string? CausationId); -} \ No newline at end of file +public record EventMetadata( + string EventId, + EventStreamId StreamId, + DateTimeOffset Timestamp, + long Version, + string? CorrelationId, + string? CausationId); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/EventStreamId.cs b/src/Atc.Cosmos.EventStore.Cqrs/EventStreamId.cs index f889ffe..827f1d9 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/EventStreamId.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/EventStreamId.cs @@ -1,33 +1,29 @@ -using System; -using System.Collections.Generic; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public class EventStreamId { - public class EventStreamId - { - public const string PartSeperator = "."; + public const string PartSeperator = "."; - public EventStreamId(params string[] parts) + public EventStreamId(params string[] parts) + { + if (parts.Length == 0) { - if (parts.Length == 0) - { - throw new ArgumentException("Please specify one or more parts.", nameof(parts)); - } - - // Validate each part does not include char '.' or is "*" - Parts = parts; - Value = string.Join(PartSeperator, parts); + throw new ArgumentException("Please specify one or more parts.", nameof(parts)); } - public IReadOnlyList Parts { get; } + // Validate each part does not include char '.' or is "*" + Parts = parts; + Value = string.Join(PartSeperator, parts); + } - public string Value { get; } + public IReadOnlyList Parts { get; } - public static implicit operator EventStreamId(StreamId id) - => FromStreamId(id.Value); + public string Value { get; } - public static EventStreamId FromStreamId(StreamId id) - => new( - id.Value.Split(PartSeperator)); - } + public static implicit operator EventStreamId(StreamId id) + => FromStreamId(id.Value); + + public static EventStreamId FromStreamId(StreamId id) + => new( + id.Value.Split(PartSeperator)); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/EventStreamVersion.cs b/src/Atc.Cosmos.EventStore.Cqrs/EventStreamVersion.cs index 6d85d8d..6ba2c84 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/EventStreamVersion.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/EventStreamVersion.cs @@ -1,66 +1,63 @@ -using System; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public struct EventStreamVersion : IEquatable { - public struct EventStreamVersion : IEquatable + /// + /// Command requires a stream containing zero or more events. + /// + public const long Any = StreamVersion.AnyValue; + + /// + /// Command requires a new stream e.g. stream must be empty. + /// + public const long StreamEmpty = StreamVersion.StartOfStreamValue; + + /// + /// Command require the stream to exists e.g. stream must contain 1 or more events. + /// + public const long Exists = StreamVersion.NotEmptyValue; + + internal EventStreamVersion(long version) { - /// - /// Command requires a stream containing zero or more events. - /// - public const long Any = StreamVersion.AnyValue; - - /// - /// Command requires a new stream e.g. stream must be empty. - /// - public const long StreamEmpty = StreamVersion.StartOfStreamValue; - - /// - /// Command require the stream to exists e.g. stream must contain 1 or more events. - /// - public const long Exists = StreamVersion.NotEmptyValue; - - internal EventStreamVersion(long version) - { - // Validate value range - Value = version; - } + // Validate value range + Value = version; + } - /// - /// Gets the value of the event stream version. - /// - public long Value { get; } + /// + /// Gets the value of the event stream version. + /// + public long Value { get; } - public static implicit operator EventStreamVersion(long version) - => new(version); + public static implicit operator EventStreamVersion(long version) + => new(version); - public static explicit operator long(EventStreamVersion version) - => version.Value; + public static explicit operator long(EventStreamVersion version) + => version.Value; - public static explicit operator StreamVersion(EventStreamVersion version) - => version.Value; + public static explicit operator StreamVersion(EventStreamVersion version) + => version.Value; - public static EventStreamVersion ToEventStreamVersion(long version) - => new(version); + public static EventStreamVersion ToEventStreamVersion(long version) + => new(version); - public static StreamVersion ToStreamVersion(EventStreamVersion version) - => new(version.Value); + public static StreamVersion ToStreamVersion(EventStreamVersion version) + => new(version.Value); - public static long FromEventStreamVersion(EventStreamVersion version) - => version.Value; + public static long FromEventStreamVersion(EventStreamVersion version) + => version.Value; - public override readonly bool Equals(object? obj) - => Value.Equals(obj); + public override readonly bool Equals(object? obj) + => Value.Equals(obj); - public bool Equals(EventStreamVersion other) - => Value == other.Value; + public bool Equals(EventStreamVersion other) + => Value == other.Value; - public override int GetHashCode() - => Value.GetHashCode(); + public override int GetHashCode() + => Value.GetHashCode(); - public static bool operator ==(EventStreamVersion left, EventStreamVersion right) - => left.Equals(right); + public static bool operator ==(EventStreamVersion left, EventStreamVersion right) + => left.Equals(right); - public static bool operator !=(EventStreamVersion left, EventStreamVersion right) - => !(left == right); - } + public static bool operator !=(EventStreamVersion left, EventStreamVersion right) + => !(left == right); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IAggregateProjection.cs b/src/Atc.Cosmos.EventStore.Cqrs/IAggregateProjection.cs index 8f04504..6fe47b1 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/IAggregateProjection.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/IAggregateProjection.cs @@ -1,14 +1,10 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public interface IAggregateProjection { - public interface IAggregateProjection - { - ValueTask InitializeAsync( - CancellationToken cancellationToken); + ValueTask InitializeAsync( + CancellationToken cancellationToken); - ValueTask CompleteAsync( - CancellationToken cancellationToken); - } + ValueTask CompleteAsync( + CancellationToken cancellationToken); } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommand.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommand.cs index 35ac9cd..5f7e7c5 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ICommand.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommand.cs @@ -1,36 +1,35 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public interface ICommand { - public interface ICommand - { - /// - /// Gets the unique id of command instance. - /// - string CommandId { get; } + /// + /// Gets the unique id of command instance. + /// + string CommandId { get; } - /// - /// Gets correlation id used to track a request through various systems and services. - /// - string? CorrelationId { get; } + /// + /// Gets correlation id used to track a request through various systems and services. + /// + string? CorrelationId { get; } - /// - /// Gets the required version, when executing the command. - /// - EventStreamVersion? RequiredVersion { get; } + /// + /// Gets the required version, when executing the command. + /// + EventStreamVersion? RequiredVersion { get; } - /// - /// Gets the behavior when stream conflict occurs. - /// - OnConflict Behavior { get; } + /// + /// Gets the behavior when stream conflict occurs. + /// + OnConflict Behavior { get; } - /// - /// Gets the number of times to rerun or retry the command when receiving a conflict. - /// - int BehaviorCount { get; } + /// + /// Gets the number of times to rerun or retry the command when receiving a conflict. + /// + int BehaviorCount { get; } - /// - /// Gets the id of event stream. - /// - /// Event stream id. - EventStreamId GetEventStreamId(); - } + /// + /// Gets the id of event stream. + /// + /// Event stream id. + EventStreamId GetEventStreamId(); } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommandContext.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommandContext.cs index 9e06536..334c92a 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ICommandContext.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommandContext.cs @@ -1,9 +1,8 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public interface ICommandContext { - public interface ICommandContext - { - void AddEvent(object evt); + void AddEvent(object evt); - object? ResponseObject { get; set; } - } + object? ResponseObject { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandler.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandler.cs index 13bf049..be1cf5b 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandler.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandler.cs @@ -1,14 +1,10 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public interface ICommandHandler + where TCommand : ICommand { - public interface ICommandHandler - where TCommand : ICommand - { - ValueTask ExecuteAsync( - TCommand command, - ICommandContext context, - CancellationToken cancellationToken); - } + ValueTask ExecuteAsync( + TCommand command, + ICommandContext context, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessor.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessor.cs index 583a604..6346447 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessor.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessor.cs @@ -1,13 +1,9 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public interface ICommandProcessor + where TCommand : ICommand { - public interface ICommandProcessor - where TCommand : ICommand - { - ValueTask ExecuteAsync( - TCommand command, - CancellationToken cancellationToken); - } + ValueTask ExecuteAsync( + TCommand command, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessorFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessorFactory.cs index fcf1096..cd170e8 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessorFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommandProcessorFactory.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public interface ICommandProcessorFactory { - public interface ICommandProcessorFactory - { - ICommandProcessor Create() - where TCommand : ICommand; - } + ICommandProcessor Create() + where TCommand : ICommand; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEvent.cs b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEvent.cs index a2057d4..add2454 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEvent.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEvent.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public interface IConsumeEvent { - public interface IConsumeEvent - { - void Consume(TEvent evt, EventMetadata metadata); - } + void Consume(TEvent evt, EventMetadata metadata); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEventAsync.cs b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEventAsync.cs index 5e0656e..16d8fe8 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEventAsync.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeEventAsync.cs @@ -1,13 +1,9 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public interface IConsumeEventAsync { - public interface IConsumeEventAsync - { - Task ConsumeAsync( - TEvent evt, - EventMetadata metadata, - CancellationToken cancellationToken); - } + Task ConsumeAsync( + TEvent evt, + EventMetadata metadata, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IProjection.cs b/src/Atc.Cosmos.EventStore.Cqrs/IProjection.cs index 8417d3c..745029d 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/IProjection.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/IProjection.cs @@ -1,20 +1,15 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +public interface IProjection { - public interface IProjection - { - Task FailedAsync( - Exception exception, - CancellationToken cancellationToken); + Task FailedAsync( + Exception exception, + CancellationToken cancellationToken); - Task InitializeAsync( - EventStreamId id, - CancellationToken cancellationToken); + Task InitializeAsync( + EventStreamId id, + CancellationToken cancellationToken); - Task CompleteAsync( - CancellationToken cancellationToken); - } + Task CompleteAsync( + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs index 06121de..23b15b5 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs @@ -1,86 +1,80 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -namespace Atc.Cosmos.EventStore.Cqrs.Internal +namespace Atc.Cosmos.EventStore.Cqrs.Internal; + +public abstract class ConsumeEventMetadata { - public abstract class ConsumeEventMetadata - { - private readonly Dictionary consumeEvents; + private readonly Dictionary consumeEvents; - protected ConsumeEventMetadata( - Type type) - { - consumeEvents = type - .GetInterfaces() - .Where(t => t.IsGenericType) - .Where(t => ImplementsConsumeEventInterfaces(t.GetGenericTypeDefinition())) - .Select(t => t.GetGenericArguments()[0]) - .Distinct() - .ToDictionary( - t => t, - t => typeof(ConsumeEventMetadata) - .GetRuntimeMethods() - .First(m => m.Name.Equals(nameof(ProjectTypedEvent), StringComparison.OrdinalIgnoreCase)) - .MakeGenericMethod(t)); - } + protected ConsumeEventMetadata( + Type type) + { + consumeEvents = type + .GetInterfaces() + .Where(t => t.IsGenericType) + .Where(t => ImplementsConsumeEventInterfaces(t.GetGenericTypeDefinition())) + .Select(t => t.GetGenericArguments()[0]) + .Distinct() + .ToDictionary( + t => t, + t => typeof(ConsumeEventMetadata) + .GetRuntimeMethods() + .First(m => m.Name.Equals(nameof(ProjectTypedEvent), StringComparison.OrdinalIgnoreCase)) + .MakeGenericMethod(t)); + } - public bool CanConsumeEvent(IEvent evt) - => consumeEvents - .ContainsKey(evt.Data.GetType()); + public bool CanConsumeEvent(IEvent evt) + => consumeEvents + .ContainsKey(evt.Data.GetType()); - public bool IsNotConsumingEvents() - => consumeEvents.Keys.Count == 0; + public bool IsNotConsumingEvents() + => consumeEvents.Keys.Count == 0; - protected async ValueTask ConsumeAsync( - IEvent evt, - object projection, - CancellationToken cancellationToken) + protected async ValueTask ConsumeAsync( + IEvent evt, + object projection, + CancellationToken cancellationToken) + { + if (!CanConsumeEvent(evt)) { - if (!CanConsumeEvent(evt)) - { - return; - } + return; + } - var metadata = new EventMetadata( - evt.Metadata.EventId, - EventStreamId.FromStreamId(evt.Metadata.StreamId), - evt.Metadata.Timestamp, - (long)evt.Metadata.Version, - CorrelationId: evt.Metadata.CorrelationId, - CausationId: evt.Metadata.CausationId); + var metadata = new EventMetadata( + evt.Metadata.EventId, + EventStreamId.FromStreamId(evt.Metadata.StreamId), + evt.Metadata.Timestamp, + (long)evt.Metadata.Version, + CorrelationId: evt.Metadata.CorrelationId, + CausationId: evt.Metadata.CausationId); - var response = consumeEvents[evt.Data.GetType()] - .Invoke(null, new object[] { projection, evt.Data, metadata, cancellationToken }); + var response = consumeEvents[evt.Data.GetType()] + .Invoke(null, new object[] { projection, evt.Data, metadata, cancellationToken }); - if (response is ValueTask v) - { - await v.ConfigureAwait(false); - } + if (response is ValueTask v) + { + await v.ConfigureAwait(false); } + } - private static bool ImplementsConsumeEventInterfaces(Type genericTypeDefinition) - => genericTypeDefinition == typeof(IConsumeEvent<>) - || genericTypeDefinition == typeof(IConsumeEventAsync<>); + private static bool ImplementsConsumeEventInterfaces(Type genericTypeDefinition) + => genericTypeDefinition == typeof(IConsumeEvent<>) + || genericTypeDefinition == typeof(IConsumeEventAsync<>); - private static async ValueTask ProjectTypedEvent( - object projection, - TEvent evt, - EventMetadata metadata, - CancellationToken cancellationToken) - where TEvent : class - { - (projection as IConsumeEvent)?.Consume(evt, metadata); + private static async ValueTask ProjectTypedEvent( + object projection, + TEvent evt, + EventMetadata metadata, + CancellationToken cancellationToken) + where TEvent : class + { + (projection as IConsumeEvent)?.Consume(evt, metadata); - if (projection is IConsumeEventAsync consumeAsync) - { - await consumeAsync - .ConsumeAsync(evt, metadata, cancellationToken) - .ConfigureAwait(false); - } + if (projection is IConsumeEventAsync consumeAsync) + { + await consumeAsync + .ConsumeAsync(evt, metadata, cancellationToken) + .ConfigureAwait(false); } } } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializer.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializer.cs index 3abc1f5..5bb56d8 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializer.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializer.cs @@ -1,17 +1,13 @@ -using System; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Internal; -namespace Atc.Cosmos.EventStore.Cqrs.Internal +internal class DependencyInitializer : IDependencyInitializer { - internal class DependencyInitializer : IDependencyInitializer - { - private readonly Task initializer; + private readonly Task initializer; - public DependencyInitializer( - Func task) - => initializer = task(); + public DependencyInitializer( + Func task) + => initializer = task(); - public Task EnsureInitializeAsync() - => initializer; - } + public Task EnsureInitializeAsync() + => initializer; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializerJob.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializerJob.cs index 5314fee..0fc706d 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializerJob.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/DependencyInitializerJob.cs @@ -1,21 +1,18 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Hosting; -namespace Atc.Cosmos.EventStore.Cqrs.Internal +namespace Atc.Cosmos.EventStore.Cqrs.Internal; + +internal class DependencyInitializerJob : IHostedService { - internal class DependencyInitializerJob : IHostedService - { - private readonly IDependencyInitializer initializer; + private readonly IDependencyInitializer initializer; - public DependencyInitializerJob( - IDependencyInitializer initializer) - => this.initializer = initializer; + public DependencyInitializerJob( + IDependencyInitializer initializer) + => this.initializer = initializer; - public Task StartAsync(CancellationToken cancellationToken) - => initializer.EnsureInitializeAsync(); + public Task StartAsync(CancellationToken cancellationToken) + => initializer.EnsureInitializeAsync(); - public Task StopAsync(CancellationToken cancellationToken) - => Task.CompletedTask; - } + public Task StopAsync(CancellationToken cancellationToken) + => Task.CompletedTask; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/IDependencyInitializer.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/IDependencyInitializer.cs index b4fb318..4089396 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/IDependencyInitializer.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/IDependencyInitializer.cs @@ -1,9 +1,6 @@ -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Internal; -namespace Atc.Cosmos.EventStore.Cqrs.Internal +internal interface IDependencyInitializer { - internal interface IDependencyInitializer - { - Task EnsureInitializeAsync(); - } + Task EnsureInitializeAsync(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/OnConflict.cs b/src/Atc.Cosmos.EventStore.Cqrs/OnConflict.cs index a2677ad..09f5ae8 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/OnConflict.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/OnConflict.cs @@ -1,23 +1,22 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +/// +/// Defines the behavior when writing to a stream results in a conflict. +/// +public enum OnConflict { /// - /// Defines the behavior when writing to a stream results in a conflict. + /// Fail command on stream write conflict. /// - public enum OnConflict - { - /// - /// Fail command on stream write conflict. - /// - Fail, + Fail, - /// - /// Retry writing events on stream write conflict. - /// - Retry, + /// + /// Retry writing events on stream write conflict. + /// + Retry, - /// - /// Rerun command on stream write conflict. - /// - RerunCommand, - } + /// + /// Rerun command on stream write conflict. + /// + RerunCommand, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ProjectionAction.cs b/src/Atc.Cosmos.EventStore.Cqrs/ProjectionAction.cs index 4500e65..882e8e1 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ProjectionAction.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ProjectionAction.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public enum ProjectionAction { - public enum ProjectionAction - { - Continue = 0, - Stop, - } + Continue = 0, + Stop, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ProjectionFilterAttribute.cs b/src/Atc.Cosmos.EventStore.Cqrs/ProjectionFilterAttribute.cs index d4c52ac..58dd807 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ProjectionFilterAttribute.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ProjectionFilterAttribute.cs @@ -1,16 +1,13 @@ -using System; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] +public sealed class ProjectionFilterAttribute : Attribute { - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] - public sealed class ProjectionFilterAttribute : Attribute + public ProjectionFilterAttribute( + string filter) { - public ProjectionFilterAttribute( - string filter) - { - Filter = filter; - } - - public string Filter { get; } + Filter = filter; } + + public string Filter { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionFactory.cs index 328b894..f51535b 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionFactory.cs @@ -1,11 +1,10 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +internal interface IProjectionFactory { - internal interface IProjectionFactory - { - IProjection GetProjection() - where TProjection : IProjection; + IProjection GetProjection() + where TProjection : IProjection; - IProjectionMetadata GetProjectionMetadata() - where TProjection : IProjection; - } + IProjectionMetadata GetProjectionMetadata() + where TProjection : IProjection; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionJob.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionJob.cs index bdbb150..7886cf3 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionJob.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionJob.cs @@ -1,18 +1,15 @@ using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +[SuppressMessage( + "Major Code Smell", + "S2326:Unused type parameters should be removed", + Justification = "Interface is used with DI to distinguish one projection from another")] +internal interface IProjectionJob + where TProjection : IProjection { - [SuppressMessage( - "Major Code Smell", - "S2326:Unused type parameters should be removed", - Justification = "Interface is used with DI to distinguish one projection from another")] - internal interface IProjectionJob - where TProjection : IProjection - { - Task StartAsync(CancellationToken cancellationToken); + Task StartAsync(CancellationToken cancellationToken); - Task StopAsync(CancellationToken cancellationToken); - } + Task StopAsync(CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionMetadata.cs index 39b0ee4..e4255d3 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionMetadata.cs @@ -1,22 +1,17 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Projections; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +public interface IProjectionMetadata { - public interface IProjectionMetadata - { - bool CanConsumeEvent( - IEvent evt); + bool CanConsumeEvent( + IEvent evt); - bool CanConsumeOneOrMoreEvents( - IEnumerable events); + bool CanConsumeOneOrMoreEvents( + IEnumerable events); - bool IsNotConsumingEvents(); + bool IsNotConsumingEvents(); - ValueTask ConsumeEventsAsync( - IEnumerable events, - IProjection projection, - CancellationToken cancellationToken); - } + ValueTask ConsumeEventsAsync( + IEnumerable events, + IProjection projection, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptions.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptions.cs index d326c95..0f52bac 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptions.cs @@ -1,13 +1,10 @@ -using System.Collections.Generic; +namespace Atc.Cosmos.EventStore.Cqrs.Projections; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +internal interface IProjectionOptions { - internal interface IProjectionOptions - { - string Name { get; } + string Name { get; } - IReadOnlyCollection Filters { get; } + IReadOnlyCollection Filters { get; } - ProcessExceptionHandler ExceptionHandler { get; } - } + ProcessExceptionHandler ExceptionHandler { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptionsFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptionsFactory.cs index 1b8a1fd..6897a63 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptionsFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionOptionsFactory.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +internal interface IProjectionOptionsFactory { - internal interface IProjectionOptionsFactory - { - IProjectionOptions GetOptions() - where TProjection : IProjection; - } + IProjectionOptions GetOptions() + where TProjection : IProjection; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionProcessor.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionProcessor.cs index 6c6a4c4..10dae72 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionProcessor.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/IProjectionProcessor.cs @@ -1,19 +1,15 @@ -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +[SuppressMessage( + "Major Code Smell", + "S2326:Unused type parameters should be removed", + Justification = "Interface is used with DI to distinguish one projection processor from another")] +internal interface IProjectionProcessor + where TProjection : IProjection { - [SuppressMessage( - "Major Code Smell", - "S2326:Unused type parameters should be removed", - Justification = "Interface is used with DI to distinguish one projection processor from another")] - internal interface IProjectionProcessor - where TProjection : IProjection - { - Task ProcessBatchAsync( - IEnumerable batch, - CancellationToken cancellationToken); - } + Task ProcessBatchAsync( + IEnumerable batch, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFactory.cs index 8f105f9..5ba18f1 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFactory.cs @@ -1,21 +1,19 @@ -using System; using Microsoft.Extensions.DependencyInjection; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +internal class ProjectionFactory : IProjectionFactory { - internal class ProjectionFactory : IProjectionFactory - { - private readonly IServiceProvider serviceProvider; + private readonly IServiceProvider serviceProvider; - public ProjectionFactory(IServiceProvider serviceProvider) - => this.serviceProvider = serviceProvider; + public ProjectionFactory(IServiceProvider serviceProvider) + => this.serviceProvider = serviceProvider; - public IProjection GetProjection() - where TProjection : IProjection - => serviceProvider.GetRequiredService(); + public IProjection GetProjection() + where TProjection : IProjection + => serviceProvider.GetRequiredService(); - public IProjectionMetadata GetProjectionMetadata() - where TProjection : IProjection - => serviceProvider.GetRequiredService>(); - } + public IProjectionMetadata GetProjectionMetadata() + where TProjection : IProjection + => serviceProvider.GetRequiredService>(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFilter.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFilter.cs index e946b2b..fd1e907 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFilter.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionFilter.cs @@ -1,54 +1,49 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace Atc.Cosmos.EventStore.Cqrs.Projections; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +internal class ProjectionFilter { - internal class ProjectionFilter + private readonly IReadOnlyList> validators; + private readonly bool endsOnAcceptAll; + + public ProjectionFilter(string filter) { - private readonly IReadOnlyList> validators; - private readonly bool endsOnAcceptAll; + validators = filter + .Split( + new[] { EventStreamId.PartSeperator }, + StringSplitOptions.RemoveEmptyEntries) + .Select(p => (p == "*" || p == "**") + ? CreateEvaluateAll() + : CreateEvaluation(p)) + .ToArray(); + endsOnAcceptAll = filter + .Split( + new[] { EventStreamId.PartSeperator }, + StringSplitOptions.RemoveEmptyEntries) + .Last() == "**"; + } - public ProjectionFilter(string filter) - { - validators = filter - .Split( - new[] { EventStreamId.PartSeperator }, - StringSplitOptions.RemoveEmptyEntries) - .Select(p => (p == "*" || p == "**") - ? CreateEvaluateAll() - : CreateEvaluation(p)) - .ToArray(); - endsOnAcceptAll = filter - .Split( - new[] { EventStreamId.PartSeperator }, - StringSplitOptions.RemoveEmptyEntries) - .Last() == "**"; - } + public bool Evaluate(StreamId streamId) + { + var parts = EventStreamId + .FromStreamId(streamId) + .Parts; - public bool Evaluate(StreamId streamId) + var index = 0; + foreach (var eval in validators) { - var parts = EventStreamId - .FromStreamId(streamId) - .Parts; - - var index = 0; - foreach (var eval in validators) + if (index >= parts.Count || !eval(parts[index++])) { - if (index >= parts.Count || !eval(parts[index++])) - { - return false; - } + return false; } - - return parts.Count <= validators.Count - || endsOnAcceptAll; } - private static Func CreateEvaluation(string expected) - => (input) => expected == input; - - private static Func CreateEvaluateAll() - => (input) => true; + return parts.Count <= validators.Count + || endsOnAcceptAll; } + + private static Func CreateEvaluation(string expected) + => (input) => expected == input; + + private static Func CreateEvaluateAll() + => (input) => true; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionJob.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionJob.cs index 34486ae..464caff 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionJob.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionJob.cs @@ -1,67 +1,63 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs.Internal; using Microsoft.Extensions.Hosting; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +internal class ProjectionJob : + IHostedService, + IProjectionJob + where TProjection : IProjection { - internal class ProjectionJob : - IHostedService, - IProjectionJob - where TProjection : IProjection + private readonly IStreamSubscription subscription; + private readonly IDependencyInitializer jobDependencies; + private readonly IProjectionProcessor processor; + + public ProjectionJob( + IEventStoreClient client, + IDependencyInitializer initializer, + IProjectionOptionsFactory optionsFactory, + IProjectionProcessor processor) { - private readonly IStreamSubscription subscription; - private readonly IDependencyInitializer jobDependencies; - private readonly IProjectionProcessor processor; - - public ProjectionJob( - IEventStoreClient client, - IDependencyInitializer initializer, - IProjectionOptionsFactory optionsFactory, - IProjectionProcessor processor) - { - this.jobDependencies = initializer; - this.processor = processor; + this.jobDependencies = initializer; + this.processor = processor; + + var options = optionsFactory.GetOptions(); + subscription = client.SubscribeToStreams( + ConsumerGroup.GetAsAutoScalingInstance(options.Name), + SubscriptionStartOptions.FromBegining, + OnProcessEventsAsync, + options.ExceptionHandler); + } - var options = optionsFactory.GetOptions(); - subscription = client.SubscribeToStreams( - ConsumerGroup.GetAsAutoScalingInstance(options.Name), - SubscriptionStartOptions.FromBegining, - OnProcessEventsAsync, - options.ExceptionHandler); - } + public async Task StartAsync(CancellationToken cancellationToken) + { + await jobDependencies + .EnsureInitializeAsync() + .ConfigureAwait(false); - public async Task StartAsync(CancellationToken cancellationToken) - { - await jobDependencies - .EnsureInitializeAsync() - .ConfigureAwait(false); + await subscription + .StartAsync() + .ConfigureAwait(false); + } - await subscription - .StartAsync() - .ConfigureAwait(false); - } + public Task StopAsync(CancellationToken cancellationToken) + => subscription.StopAsync(); - public Task StopAsync(CancellationToken cancellationToken) - => subscription.StopAsync(); + private async Task OnProcessEventsAsync( + IEnumerable events, + CancellationToken cancellationToken) + { + var action = await processor + .ProcessBatchAsync( + events, + cancellationToken) + .ConfigureAwait(false); - private async Task OnProcessEventsAsync( - IEnumerable events, - CancellationToken cancellationToken) + if (action == ProjectionAction.Stop) { - var action = await processor - .ProcessBatchAsync( - events, - cancellationToken) + await subscription + .StopAsync() .ConfigureAwait(false); - - if (action == ProjectionAction.Stop) - { - await subscription - .StopAsync() - .ConfigureAwait(false); - } } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionMetadata.cs index a884c24..31d6954 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionMetadata.cs @@ -1,37 +1,32 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs.Internal; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +public class ProjectionMetadata : + ConsumeEventMetadata, + IProjectionMetadata + where TProjection : IProjection { - public class ProjectionMetadata : - ConsumeEventMetadata, - IProjectionMetadata - where TProjection : IProjection + public ProjectionMetadata() + : base(typeof(TProjection)) { - public ProjectionMetadata() - : base(typeof(TProjection)) - { - } + } - public bool CanConsumeOneOrMoreEvents(IEnumerable events) - => events.Any(evt => CanConsumeEvent(evt)); + public bool CanConsumeOneOrMoreEvents(IEnumerable events) + => events.Any(evt => CanConsumeEvent(evt)); - public async ValueTask ConsumeEventsAsync( - IEnumerable events, - IProjection projection, - CancellationToken cancellationToken) + public async ValueTask ConsumeEventsAsync( + IEnumerable events, + IProjection projection, + CancellationToken cancellationToken) + { + foreach (var evt in events) { - foreach (var evt in events) - { - await ConsumeAsync( - evt, - projection, - cancellationToken) - .ConfigureAwait(false); - } + await ConsumeAsync( + evt, + projection, + cancellationToken) + .ConfigureAwait(false); } } } diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptions.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptions.cs index d8decd9..1506748 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptions.cs @@ -1,25 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Projections; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +internal class ProjectionOptions : IProjectionOptions { - internal class ProjectionOptions : IProjectionOptions - { - public static readonly ProcessExceptionHandler EmptyExceptionHandler = (e, ct) - => Task.CompletedTask; + public static readonly ProcessExceptionHandler EmptyExceptionHandler = (e, ct) + => Task.CompletedTask; - public ProjectionOptions() - { - Name = string.Empty; - Filters = Array.Empty(); - ExceptionHandler = EmptyExceptionHandler; - } + public ProjectionOptions() + { + Name = string.Empty; + Filters = Array.Empty(); + ExceptionHandler = EmptyExceptionHandler; + } - public string Name { get; set; } + public string Name { get; set; } - public IReadOnlyCollection Filters { get; set; } + public IReadOnlyCollection Filters { get; set; } - public ProcessExceptionHandler ExceptionHandler { get; set; } - } + public ProcessExceptionHandler ExceptionHandler { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptionsFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptionsFactory.cs index ae2f1b9..3c9726c 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptionsFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionOptionsFactory.cs @@ -1,20 +1,19 @@ using Microsoft.Extensions.Options; -namespace Atc.Cosmos.EventStore.Cqrs.Projections -{ - internal class ProjectionOptionsFactory : IProjectionOptionsFactory - { - private readonly IOptionsMonitor namedOptions; +namespace Atc.Cosmos.EventStore.Cqrs.Projections; - public ProjectionOptionsFactory( - IOptionsMonitor namedOptions) - { - this.namedOptions = namedOptions; - } +internal class ProjectionOptionsFactory : IProjectionOptionsFactory +{ + private readonly IOptionsMonitor namedOptions; - public IProjectionOptions GetOptions() - where TProjection : IProjection - => namedOptions.Get( - typeof(TProjection).Name); + public ProjectionOptionsFactory( + IOptionsMonitor namedOptions) + { + this.namedOptions = namedOptions; } + + public IProjectionOptions GetOptions() + where TProjection : IProjection + => namedOptions.Get( + typeof(TProjection).Name); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionProcessor.cs b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionProcessor.cs index 9e71b40..3afe562 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionProcessor.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Projections/ProjectionProcessor.cs @@ -1,100 +1,94 @@ -using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs.Diagnostics; -namespace Atc.Cosmos.EventStore.Cqrs.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Projections; + +internal class ProjectionProcessor : IProjectionProcessor + where TProjection : IProjection { - internal class ProjectionProcessor : IProjectionProcessor - where TProjection : IProjection + private readonly IReadOnlyCollection filters; + private readonly IProjectionFactory projectionFactory; + private readonly IProjectionDiagnostics diagnostics; + private readonly IProjectionMetadata projectionMetadata; + private readonly string projectionName; + + public ProjectionProcessor( + IProjectionOptionsFactory optionsFactory, + IProjectionFactory projectionFactory, + IProjectionDiagnostics diagnostics) { - private readonly IReadOnlyCollection filters; - private readonly IProjectionFactory projectionFactory; - private readonly IProjectionDiagnostics diagnostics; - private readonly IProjectionMetadata projectionMetadata; - private readonly string projectionName; + this.projectionFactory = projectionFactory; + this.diagnostics = diagnostics; + filters = optionsFactory + .GetOptions() + .Filters; + projectionMetadata = projectionFactory + .GetProjectionMetadata(); + projectionName = typeof(TProjection).Name; + } - public ProjectionProcessor( - IProjectionOptionsFactory optionsFactory, - IProjectionFactory projectionFactory, - IProjectionDiagnostics diagnostics) - { - this.projectionFactory = projectionFactory; - this.diagnostics = diagnostics; - filters = optionsFactory - .GetOptions() - .Filters; - projectionMetadata = projectionFactory - .GetProjectionMetadata(); - projectionName = typeof(TProjection).Name; - } + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "By Design")] + public async Task ProcessBatchAsync( + IEnumerable batch, + CancellationToken cancellationToken) + { + var projection = projectionFactory + .GetProjection(); - [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "By Design")] - public async Task ProcessBatchAsync( - IEnumerable batch, - CancellationToken cancellationToken) + try { - var projection = projectionFactory - .GetProjection(); - - try - { - var groupedEvents = batch - .Where(e => filters.Any(f => f.Evaluate(e.Metadata.StreamId))) - .GroupBy(e => e.Metadata.StreamId) - .ToArray(); + var groupedEvents = batch + .Where(e => filters.Any(f => f.Evaluate(e.Metadata.StreamId))) + .GroupBy(e => e.Metadata.StreamId) + .ToArray(); - diagnostics.ProcessingGroupedEvents(projectionName, groupedEvents, batch); + diagnostics.ProcessingGroupedEvents(projectionName, groupedEvents, batch); - foreach (var events in groupedEvents) + foreach (var events in groupedEvents) + { + var operation = diagnostics.StartStreamProjection(projectionName, events.Key); + if (!projectionMetadata.CanConsumeOneOrMoreEvents(events)) { - var operation = diagnostics.StartStreamProjection(projectionName, events.Key); - if (!projectionMetadata.CanConsumeOneOrMoreEvents(events)) - { - // Skip if projection is not consuming any of the events. - operation.ProjectionSkipped(events); - continue; - } + // Skip if projection is not consuming any of the events. + operation.ProjectionSkipped(events); + continue; + } - try - { - await projection - .InitializeAsync( - events.Key, - cancellationToken) - .ConfigureAwait(false); + try + { + await projection + .InitializeAsync( + events.Key, + cancellationToken) + .ConfigureAwait(false); - await projectionMetadata - .ConsumeEventsAsync(events, projection, cancellationToken) - .ConfigureAwait(false); + await projectionMetadata + .ConsumeEventsAsync(events, projection, cancellationToken) + .ConfigureAwait(false); - await projection - .CompleteAsync(cancellationToken) - .ConfigureAwait(false); + await projection + .CompleteAsync(cancellationToken) + .ConfigureAwait(false); - operation.ProjectionCompleted(events); - } - catch (Exception ex) - { - operation.ProjectionFailed(events, ex); + operation.ProjectionCompleted(events); + } + catch (Exception ex) + { + operation.ProjectionFailed(events, ex); - return await projection - .FailedAsync(ex, cancellationToken) - .ConfigureAwait(false); - } + return await projection + .FailedAsync(ex, cancellationToken) + .ConfigureAwait(false); } } - catch (Exception ex) - { - return await projection - .FailedAsync(ex, cancellationToken) - .ConfigureAwait(false); - } - - return ProjectionAction.Continue; } + catch (Exception ex) + { + return await projection + .FailedAsync(ex, cancellationToken) + .ConfigureAwait(false); + } + + return ProjectionAction.Continue; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ResultType.cs b/src/Atc.Cosmos.EventStore.Cqrs/ResultType.cs index 662b8f2..e3773e6 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/ResultType.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/ResultType.cs @@ -1,30 +1,29 @@ -namespace Atc.Cosmos.EventStore.Cqrs +namespace Atc.Cosmos.EventStore.Cqrs; + +public enum ResultType { - public enum ResultType - { - /// - /// Command resulted in successfully writing one or more events to stream. - /// - Changed, + /// + /// Command resulted in successfully writing one or more events to stream. + /// + Changed, - /// - /// Command yielded no events to write to stream. - /// - NotModified, + /// + /// Command yielded no events to write to stream. + /// + NotModified, - /// - /// Current stream version was not at the required position. - /// - Conflict, + /// + /// Current stream version was not at the required position. + /// + Conflict, - /// - /// Command failed as it required the stream to container one or more events, but an empty stream was found. - /// - NotFound, + /// + /// Command failed as it required the stream to container one or more events, but an empty stream was found. + /// + NotFound, - /// - /// Command failed as it required the stream to be empty, but one or more events was found. - /// - Exists, - } + /// + /// Command failed as it required the stream to be empty, but one or more events was found. + /// + Exists, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/StreamEventAttribute.cs b/src/Atc.Cosmos.EventStore.Cqrs/StreamEventAttribute.cs index b8dbca5..280a778 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/StreamEventAttribute.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/StreamEventAttribute.cs @@ -1,15 +1,12 @@ -using System; +namespace Atc.Cosmos.EventStore.Cqrs; -namespace Atc.Cosmos.EventStore.Cqrs +[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +public sealed class StreamEventAttribute : Attribute { - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - public sealed class StreamEventAttribute : Attribute + public StreamEventAttribute(string name) { - public StreamEventAttribute(string name) - { - Name = name; - } - - public EventName Name { get; } + Name = name; } + + public EventName Name { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerExtensions.cs index 8a26448..5173185 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerExtensions.cs @@ -1,12 +1,11 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Testing +namespace Atc.Cosmos.EventStore.Cqrs.Testing; + +public static class CommandHandlerExtensions { - public static class CommandHandlerExtensions - { - public static ICommandWhen GivenStreamContainingEvents( - this ICommandHandler handler, - params object[] events) - where TCommand : ICommand - => new CommandHandlerTester(handler) - .GivenEvents(events); - } + public static ICommandWhen GivenStreamContainingEvents( + this ICommandHandler handler, + params object[] events) + where TCommand : ICommand + => new CommandHandlerTester(handler) + .GivenEvents(events); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs index c116f74..887f37f 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs @@ -1,138 +1,133 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cqrs.Commands; using Atc.Cosmos.EventStore.Cqrs.Internal; using Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.Cqrs.Testing +namespace Atc.Cosmos.EventStore.Cqrs.Testing; + +internal class CommandHandlerTester : + ICommandGiven, + ICommandWhen, + ICommandThen + where TCommand : ICommand { - internal class CommandHandlerTester : - ICommandGiven, - ICommandWhen, - ICommandThen - where TCommand : ICommand + private sealed class TestMetadata : + ConsumeEventMetadata { - private sealed class TestMetadata : - ConsumeEventMetadata + public TestMetadata(object handler) + : base(handler.GetType()) { - public TestMetadata(object handler) - : base(handler.GetType()) - { - } - - public ValueTask ApplyEventAsync( - IEvent evt, - object handler, - CancellationToken cancellationToken) - => ConsumeAsync(evt, handler, cancellationToken); } - private readonly ICommandHandler handler; - private readonly TestMetadata handlerMetadata; - private readonly List events; - private TCommand? command; + public ValueTask ApplyEventAsync( + IEvent evt, + object handler, + CancellationToken cancellationToken) + => ConsumeAsync(evt, handler, cancellationToken); + } - public CommandHandlerTester( - ICommandHandler handler) - { - this.handler = handler; - this.handlerMetadata = new TestMetadata(handler); - this.events = new List(); - } + private readonly ICommandHandler handler; + private readonly TestMetadata handlerMetadata; + private readonly List events; + private TCommand? command; - public ICommandWhen GivenEvents(params object[] events) - { - this.events.AddRange(events); + public CommandHandlerTester( + ICommandHandler handler) + { + this.handler = handler; + this.handlerMetadata = new TestMetadata(handler); + this.events = new List(); + } - return this; - } + public ICommandWhen GivenEvents(params object[] events) + { + this.events.AddRange(events); - public ICommandThen WhenExecuting(TCommand command) - { - this.command = command; + return this; + } - return this; - } + public ICommandThen WhenExecuting(TCommand command) + { + this.command = command; - public async Task ThenExpectEvents( - Action assert, - CancellationToken cancellationToken = default) - => assert.Invoke( - await ExecuteAsync( - cancellationToken) - .ConfigureAwait(false)); + return this; + } - public async Task ThenExpectException( - CancellationToken cancellationToken = default) - where TException : Exception + public async Task ThenExpectEvents( + Action assert, + CancellationToken cancellationToken = default) + => assert.Invoke( + await ExecuteAsync( + cancellationToken) + .ConfigureAwait(false)); + + public async Task ThenExpectException( + CancellationToken cancellationToken = default) + where TException : Exception + { + try { - try - { - await ExecuteAsync(cancellationToken) - .ConfigureAwait(false); - } - catch (TException) - { - return; - } - - throw new InvalidOperationException($"{typeof(TException).Name} not thrown"); + await ExecuteAsync(cancellationToken) + .ConfigureAwait(false); } + catch (TException) + { + return; + } + + throw new InvalidOperationException($"{typeof(TException).Name} not thrown"); + } - public async Task ThenExpectException( - Predicate predicate, - CancellationToken cancellationToken = default) - where TException : Exception + public async Task ThenExpectException( + Predicate predicate, + CancellationToken cancellationToken = default) + where TException : Exception + { + try + { + await ExecuteAsync(cancellationToken) + .ConfigureAwait(false); + } + catch (TException ex) + when (predicate(ex)) { - try - { - await ExecuteAsync(cancellationToken) - .ConfigureAwait(false); - } - catch (TException ex) - when (predicate(ex)) - { - return; - } - - throw new InvalidOperationException($"{typeof(TException).Name} not thrown"); + return; } - private async Task ExecuteAsync(CancellationToken cancellationToken) + throw new InvalidOperationException($"{typeof(TException).Name} not thrown"); + } + + private async Task ExecuteAsync(CancellationToken cancellationToken) + { + var version = 1; + foreach (var evt in events) { - var version = 1; - foreach (var evt in events) - { - await handlerMetadata - .ApplyEventAsync( - new EventDocument + await handlerMetadata + .ApplyEventAsync( + new EventDocument + { + Data = evt, + Properties = new Events.EventMetadata { - Data = evt, - Properties = new Events.EventMetadata - { - CausationId = Guid.NewGuid().ToString(), - CorrelationId = Guid.NewGuid().ToString(), - EventId = Guid.NewGuid().ToString(), - Name = "test", - Version = version++, - Timestamp = DateTimeOffset.UtcNow, - StreamId = "test-stream", - }, + CausationId = Guid.NewGuid().ToString(), + CorrelationId = Guid.NewGuid().ToString(), + EventId = Guid.NewGuid().ToString(), + Name = "test", + Version = version++, + Timestamp = DateTimeOffset.UtcNow, + StreamId = "test-stream", }, - handler, - cancellationToken) - .ConfigureAwait(false); - } + }, + handler, + cancellationToken) + .ConfigureAwait(false); + } - var context = new CommandContext(); + var context = new CommandContext(); - await handler - .ExecuteAsync(command!, context, cancellationToken) - .ConfigureAwait(false); + await handler + .ExecuteAsync(command!, context, cancellationToken) + .ConfigureAwait(false); - return context; - } + return context; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandContextInspector.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandContextInspector.cs index 9b03f70..a54064b 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandContextInspector.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandContextInspector.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; +namespace Atc.Cosmos.EventStore.Cqrs.Testing; -namespace Atc.Cosmos.EventStore.Cqrs.Testing +public interface ICommandContextInspector { - public interface ICommandContextInspector - { - IReadOnlyCollection Events { get; } + IReadOnlyCollection Events { get; } - object? ResponseObject { get; } - } + object? ResponseObject { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandGiven.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandGiven.cs index 09ae407..e39b546 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandGiven.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandGiven.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Testing +namespace Atc.Cosmos.EventStore.Cqrs.Testing; + +public interface ICommandGiven + where TCommand : ICommand { - public interface ICommandGiven - where TCommand : ICommand - { - ICommandWhen GivenEvents(params object[] events); - } + ICommandWhen GivenEvents(params object[] events); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandThen.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandThen.cs index c3ecdb8..c4dad53 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandThen.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandThen.cs @@ -1,22 +1,17 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Testing; -namespace Atc.Cosmos.EventStore.Cqrs.Testing +public interface ICommandThen { - public interface ICommandThen - { - Task ThenExpectEvents( - Action assert, - CancellationToken cancellationToken = default); + Task ThenExpectEvents( + Action assert, + CancellationToken cancellationToken = default); - Task ThenExpectException( - CancellationToken cancellationToken = default) - where TException : Exception; + Task ThenExpectException( + CancellationToken cancellationToken = default) + where TException : Exception; - Task ThenExpectException( - Predicate predicate, - CancellationToken cancellationToken = default) - where TException : Exception; - } + Task ThenExpectException( + Predicate predicate, + CancellationToken cancellationToken = default) + where TException : Exception; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandWhen.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandWhen.cs index 397eace..c66cd53 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandWhen.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/ICommandWhen.cs @@ -1,8 +1,7 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Testing +namespace Atc.Cosmos.EventStore.Cqrs.Testing; + +public interface ICommandWhen + where TCommand : ICommand { - public interface ICommandWhen - where TCommand : ICommand - { - ICommandThen WhenExecuting(TCommand command); - } + ICommandThen WhenExecuting(TCommand command); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Arguments.cs b/src/Atc.Cosmos.EventStore/Arguments.cs index 2f77cb1..797f530 100644 --- a/src/Atc.Cosmos.EventStore/Arguments.cs +++ b/src/Atc.Cosmos.EventStore/Arguments.cs @@ -1,58 +1,54 @@ -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +[DebuggerStepThrough] +internal static class Arguments { - [DebuggerStepThrough] - internal static class Arguments + internal static IReadOnlyCollection EnsureNoNullValues(IReadOnlyCollection events, string argumentName) { - internal static IReadOnlyCollection EnsureNoNullValues(IReadOnlyCollection events, string argumentName) + if (events is null) { - if (events is null) - { - throw new ArgumentNullException(argumentName); - } - - if (events.Any(e => e is null)) - { - throw new ArgumentException("Null values not allowed", argumentName); - } - - return events; + throw new ArgumentNullException(argumentName); } - internal static object EnsureNotNull(object argumentValue, string argumentName) + if (events.Any(e => e is null)) { - if (argumentValue is null) - { - throw new ArgumentNullException(argumentName); - } - - return argumentValue; + throw new ArgumentException("Null values not allowed", argumentName); } - internal static T EnsureNotNull(T? argumentValue, string argumentName) + return events; + } + + internal static object EnsureNotNull(object argumentValue, string argumentName) + { + if (argumentValue is null) { - if (argumentValue is null) - { - throw new ArgumentNullException(argumentName); - } + throw new ArgumentNullException(argumentName); + } - return argumentValue; + return argumentValue; + } + + internal static T EnsureNotNull(T? argumentValue, string argumentName) + { + if (argumentValue is null) + { + throw new ArgumentNullException(argumentName); } - internal static StreamVersion EnsureValueRange(StreamVersion streamVersion, string argumentName) + return argumentValue; + } + + internal static StreamVersion EnsureValueRange(StreamVersion streamVersion, string argumentName) + { + if (streamVersion < StreamVersion.NotEmpty) { - if (streamVersion < StreamVersion.NotEmpty) - { - throw new ArgumentOutOfRangeException( - argumentName, - $"Stream version {streamVersion.Value} is outside of valid range [{StreamVersion.NotEmptyValue}-{StreamVersion.EndOfStreamValue}]."); - } - - return streamVersion; + throw new ArgumentOutOfRangeException( + argumentName, + $"Stream version {streamVersion.Value} is outside of valid range [{StreamVersion.NotEmptyValue}-{StreamVersion.EndOfStreamValue}]."); } + + return streamVersion; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Checkpoint.cs b/src/Atc.Cosmos.EventStore/Checkpoint.cs index a9a4267..2027d82 100644 --- a/src/Atc.Cosmos.EventStore/Checkpoint.cs +++ b/src/Atc.Cosmos.EventStore/Checkpoint.cs @@ -1,17 +1,14 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore -{ - /// - /// Represents a named checkpoint within a stream. - /// - /// Name of checkpoint. - /// Id of stream. - /// Version within the stream. - /// When the checkpoint was created. - public record Checkpoint( - string Name, - StreamId StreamId, - StreamVersion StreamVersion, - DateTimeOffset Timestamp); -} \ No newline at end of file +/// +/// Represents a named checkpoint within a stream. +/// +/// Name of checkpoint. +/// Id of stream. +/// Version within the stream. +/// When the checkpoint was created. +public record Checkpoint( + string Name, + StreamId StreamId, + StreamVersion StreamVersion, + DateTimeOffset Timestamp); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Checkpoint{TState}.cs b/src/Atc.Cosmos.EventStore/Checkpoint{TState}.cs index 59431a0..05d25cc 100644 --- a/src/Atc.Cosmos.EventStore/Checkpoint{TState}.cs +++ b/src/Atc.Cosmos.EventStore/Checkpoint{TState}.cs @@ -1,21 +1,18 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore -{ - /// - /// Represents a named checkpoint and state within a stream. - /// - /// Type of state. - /// Name of checkpoint. - /// Id of stream. - /// Version within the stream. - /// When the checkpoint was created. - /// Checkpoint state to set. - public record Checkpoint( - string Name, - StreamId StreamId, - StreamVersion StreamVersion, - DateTimeOffset Timestamp, - TState State) - : Checkpoint(Name, StreamId, StreamVersion, Timestamp); -} \ No newline at end of file +/// +/// Represents a named checkpoint and state within a stream. +/// +/// Type of state. +/// Name of checkpoint. +/// Id of stream. +/// Version within the stream. +/// When the checkpoint was created. +/// Checkpoint state to set. +public record Checkpoint( + string Name, + StreamId StreamId, + StreamVersion StreamVersion, + DateTimeOffset Timestamp, + TState State) + : Checkpoint(Name, StreamId, StreamVersion, Timestamp); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/ConsumerGroup.cs b/src/Atc.Cosmos.EventStore/ConsumerGroup.cs index 304dd84..278f0ad 100644 --- a/src/Atc.Cosmos.EventStore/ConsumerGroup.cs +++ b/src/Atc.Cosmos.EventStore/ConsumerGroup.cs @@ -1,27 +1,24 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public class ConsumerGroup { - public class ConsumerGroup - { - private const string DefaultInstance = ".default"; + private const string DefaultInstance = ".default"; - public ConsumerGroup(string name) - : this(name, DefaultInstance) - { - } + public ConsumerGroup(string name) + : this(name, DefaultInstance) + { + } - public ConsumerGroup(string name, string instance) - { - Name = name; - Instance = instance; - } + public ConsumerGroup(string name, string instance) + { + Name = name; + Instance = instance; + } - public string Name { get; } + public string Name { get; } - public string Instance { get; } + public string Instance { get; } - public static ConsumerGroup GetAsAutoScalingInstance(string name) - => new(name, Guid.NewGuid().ToString()); - } + public static ConsumerGroup GetAsAutoScalingInstance(string name) + => new(name, Guid.NewGuid().ToString()); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs index 0ef7c06..248ffe7 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDataConverterPipelineBuilder.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; namespace Atc.Cosmos.EventStore.Converters; diff --git a/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs b/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs index 1d44663..e010ec6 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventDocumentConverter.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Atc.Cosmos.EventStore.Events; diff --git a/src/Atc.Cosmos.EventStore/Converters/EventNameConverter.cs b/src/Atc.Cosmos.EventStore/Converters/EventNameConverter.cs index c708709..f93ef4f 100644 --- a/src/Atc.Cosmos.EventStore/Converters/EventNameConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/EventNameConverter.cs @@ -1,23 +1,21 @@ -using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Converters +namespace Atc.Cosmos.EventStore.Converters; + +internal class EventNameConverter : JsonConverter { - internal class EventNameConverter : JsonConverter + public override EventName Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override EventName Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var value = reader.GetString(); + if (value is null) { - var value = reader.GetString(); - if (value is null) - { - throw new JsonException(); - } - - return value; + throw new JsonException(); } - public override void Write(Utf8JsonWriter writer, EventName value, JsonSerializerOptions options) - => writer.WriteStringValue(value.Value); + return value; } + + public override void Write(Utf8JsonWriter writer, EventName value, JsonSerializerOptions options) + => writer.WriteStringValue(value.Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs index bd35160..f31e9ef 100644 --- a/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/FaultedEventDataConverter.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json; diff --git a/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs index 6cd274b..d05ed29 100644 --- a/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/IEventDataConverter.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json; diff --git a/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs index fd03357..7845ed0 100644 --- a/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/NamedEventConverter.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; using Atc.Cosmos.EventStore.Events; diff --git a/src/Atc.Cosmos.EventStore/Converters/StreamIdConverter.cs b/src/Atc.Cosmos.EventStore/Converters/StreamIdConverter.cs index 6df7cc2..66cc8af 100644 --- a/src/Atc.Cosmos.EventStore/Converters/StreamIdConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/StreamIdConverter.cs @@ -1,23 +1,21 @@ -using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Converters +namespace Atc.Cosmos.EventStore.Converters; + +internal class StreamIdConverter : JsonConverter { - internal class StreamIdConverter : JsonConverter + public override StreamId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override StreamId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var value = reader.GetString(); + if (value is null) { - var value = reader.GetString(); - if (value is null) - { - throw new JsonException(); - } - - return value; + throw new JsonException(); } - public override void Write(Utf8JsonWriter writer, StreamId value, JsonSerializerOptions options) - => writer.WriteStringValue(value.Value); + return value; } + + public override void Write(Utf8JsonWriter writer, StreamId value, JsonSerializerOptions options) + => writer.WriteStringValue(value.Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/StreamVersionConverter.cs b/src/Atc.Cosmos.EventStore/Converters/StreamVersionConverter.cs index 38dcba7..137fccb 100644 --- a/src/Atc.Cosmos.EventStore/Converters/StreamVersionConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/StreamVersionConverter.cs @@ -1,17 +1,15 @@ -using System; using System.Text.Json; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Converters +namespace Atc.Cosmos.EventStore.Converters; + +internal class StreamVersionConverter : JsonConverter { - internal class StreamVersionConverter : JsonConverter - { - public override StreamVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => reader.TryGetInt64(out var value) - ? value - : throw new JsonException(); + public override StreamVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => reader.TryGetInt64(out var value) + ? value + : throw new JsonException(); - public override void Write(Utf8JsonWriter writer, StreamVersion value, JsonSerializerOptions options) - => writer.WriteNumberValue(value.Value); - } + public override void Write(Utf8JsonWriter writer, StreamVersion value, JsonSerializerOptions options) + => writer.WriteNumberValue(value.Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/TimeSpanConverter.cs b/src/Atc.Cosmos.EventStore/Converters/TimeSpanConverter.cs index eaf95da..cae7b5a 100644 --- a/src/Atc.Cosmos.EventStore/Converters/TimeSpanConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/TimeSpanConverter.cs @@ -1,23 +1,21 @@ -using System; using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Converters +namespace Atc.Cosmos.EventStore.Converters; + +internal class TimeSpanConverter : JsonConverter { - internal class TimeSpanConverter : JsonConverter + public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var timespan = reader.GetString(); - return TimeSpan.TryParse(timespan, CultureInfo.InvariantCulture, out var ts) - ? ts - : throw new JsonException("Value is not a valid timespan format"); - } + var timespan = reader.GetString(); + return TimeSpan.TryParse(timespan, CultureInfo.InvariantCulture, out var ts) + ? ts + : throw new JsonException("Value is not a valid timespan format"); + } - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } + public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs b/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs index 9864b94..389c23f 100644 --- a/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs +++ b/src/Atc.Cosmos.EventStore/Converters/UnknownEventDataConverter.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; namespace Atc.Cosmos.EventStore.Converters; diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument.cs b/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument.cs index 1eefa6a..9551470 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument.cs @@ -1,12 +1,10 @@ -using System; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Cosmos -{ - internal record CheckpointDocument( - [property: JsonPropertyName("id")] string Name, - [property: JsonPropertyName("pk")] StreamId StreamId, - StreamVersion StreamVersion, - DateTimeOffset Timestamp, - object? State); -} \ No newline at end of file +namespace Atc.Cosmos.EventStore.Cosmos; + +internal record CheckpointDocument( + [property: JsonPropertyName("id")] string Name, + [property: JsonPropertyName("pk")] StreamId StreamId, + StreamVersion StreamVersion, + DateTimeOffset Timestamp, + object? State); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument{TState}.cs b/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument{TState}.cs index a2e30eb..908297b 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument{TState}.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CheckpointDocument{TState}.cs @@ -1,12 +1,10 @@ -using System; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Cosmos -{ - internal record CheckpointDocument( - [property: JsonPropertyName("id")] string Name, - [property: JsonPropertyName("pk")] StreamId StreamId, - StreamVersion StreamVersion, - DateTimeOffset Timestamp, - TState State); -} \ No newline at end of file +namespace Atc.Cosmos.EventStore.Cosmos; + +internal record CheckpointDocument( + [property: JsonPropertyName("id")] string Name, + [property: JsonPropertyName("pk")] StreamId StreamId, + StreamVersion StreamVersion, + DateTimeOffset Timestamp, + TState State); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosBatchWriter.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosBatchWriter.cs index 3b4b80b..1fe91ef 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosBatchWriter.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosBatchWriter.cs @@ -1,102 +1,99 @@ using System.Net; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosBatchWriter : IStreamBatchWriter { - internal class CosmosBatchWriter : IStreamBatchWriter + private readonly IEventStoreContainerProvider containerProvider; + private readonly IDateTimeProvider dateTimeProvider; + + public CosmosBatchWriter( + IEventStoreContainerProvider containerProvider, + IDateTimeProvider dateTimeProvider) + { + this.containerProvider = containerProvider; + this.dateTimeProvider = dateTimeProvider; + } + + public async Task WriteAsync( + StreamBatch batch, + CancellationToken cancellationToken) { - private readonly IEventStoreContainerProvider containerProvider; - private readonly IDateTimeProvider dateTimeProvider; + var pk = new PartitionKey(batch.Metadata.StreamId.Value); + var tx = containerProvider + .GetStreamContainer() + .CreateTransactionalBatch(pk); + + tx.UpsertItem( + batch.Metadata, + new TransactionalBatchItemRequestOptions { IfMatchEtag = batch.Metadata.ETag }); - public CosmosBatchWriter( - IEventStoreContainerProvider containerProvider, - IDateTimeProvider dateTimeProvider) + foreach (var document in batch.Documents) { - this.containerProvider = containerProvider; - this.dateTimeProvider = dateTimeProvider; + tx.CreateItem( + document, + new TransactionalBatchItemRequestOptions { EnableContentResponseOnWrite = false }); } - public async Task WriteAsync( - StreamBatch batch, - CancellationToken cancellationToken) + if (IsEmptyStream(batch.Metadata)) { - var pk = new PartitionKey(batch.Metadata.StreamId.Value); - var tx = containerProvider - .GetStreamContainer() - .CreateTransactionalBatch(pk); - - tx.UpsertItem( - batch.Metadata, - new TransactionalBatchItemRequestOptions { IfMatchEtag = batch.Metadata.ETag }); - - foreach (var document in batch.Documents) - { - tx.CreateItem( - document, - new TransactionalBatchItemRequestOptions { EnableContentResponseOnWrite = false }); - } - - if (IsEmptyStream(batch.Metadata)) - { - await containerProvider - .GetIndexContainer() - .UpsertItemAsync( - new StreamIndex - { - StreamId = batch.Metadata.StreamId.Value, - Timestamp = dateTimeProvider.GetDateTime(), - IsActive = true, - }, - new PartitionKey(nameof(StreamIndex)), - new ItemRequestOptions { EnableContentResponseOnWrite = false }, - cancellationToken) - .ConfigureAwait(false); - } - - using var batchResponse = await tx - .ExecuteAsync(cancellationToken) + await containerProvider + .GetIndexContainer() + .UpsertItemAsync( + new StreamIndex + { + StreamId = batch.Metadata.StreamId.Value, + Timestamp = dateTimeProvider.GetDateTime(), + IsActive = true, + }, + new PartitionKey(nameof(StreamIndex)), + new ItemRequestOptions { EnableContentResponseOnWrite = false }, + cancellationToken) .ConfigureAwait(false); + } - EnsureSuccess(batchResponse, batch.Metadata); + using var batchResponse = await tx + .ExecuteAsync(cancellationToken) + .ConfigureAwait(false); - return GetMetadataFromResponse(batchResponse); - } + EnsureSuccess(batchResponse, batch.Metadata); - private static bool IsEmptyStream(StreamMetadata metadata) - => string.IsNullOrWhiteSpace(metadata.ETag); // As metadata has already been updated we can only use ETag to see if its a new stream. + return GetMetadataFromResponse(batchResponse); + } - private static IStreamMetadata GetMetadataFromResponse(TransactionalBatchResponse response) - { - var result = response.GetOperationResultAtIndex(0); - var metadata = result.Resource; + private static bool IsEmptyStream(StreamMetadata metadata) + => string.IsNullOrWhiteSpace(metadata.ETag); // As metadata has already been updated we can only use ETag to see if its a new stream. - metadata.ETag = result.ETag; + private static IStreamMetadata GetMetadataFromResponse(TransactionalBatchResponse response) + { + var result = response.GetOperationResultAtIndex(0); + var metadata = result.Resource; - return metadata; - } + metadata.ETag = result.ETag; + + return metadata; + } - private static void EnsureSuccess(TransactionalBatchResponse response, StreamMetadata metadata) + private static void EnsureSuccess(TransactionalBatchResponse response, StreamMetadata metadata) + { + if (!response.IsSuccessStatusCode) { - if (!response.IsSuccessStatusCode) + if (response.StatusCode == HttpStatusCode.TooManyRequests) { - if (response.StatusCode == HttpStatusCode.TooManyRequests) - { - throw new CosmosException( - response.ErrorMessage, - response.StatusCode, - 0, - response.ActivityId, - response.RequestCharge); - } - - throw new StreamWriteConflictException( - metadata.StreamId, - metadata.Version); + throw new CosmosException( + response.ErrorMessage, + response.StatusCode, + 0, + response.ActivityId, + response.RequestCharge); } + + throw new StreamWriteConflictException( + metadata.StreamId, + metadata.Version); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointReader.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointReader.cs index 0a7df25..78b7c79 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointReader.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointReader.cs @@ -1,46 +1,43 @@ using System.Net; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosCheckpointReader : IStreamCheckpointReader { - internal class CosmosCheckpointReader : IStreamCheckpointReader - { - private readonly IEventStoreContainerProvider containerProvider; + private readonly IEventStoreContainerProvider containerProvider; - public CosmosCheckpointReader( - IEventStoreContainerProvider containerProvider) - => this.containerProvider = containerProvider; + public CosmosCheckpointReader( + IEventStoreContainerProvider containerProvider) + => this.containerProvider = containerProvider; - public async Task?> ReadAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken) + public async Task?> ReadAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + try { - try - { - var checkpoint = await containerProvider - .GetIndexContainer() - .ReadItemAsync>( - name, - new PartitionKey(streamId.Value), - cancellationToken: cancellationToken) - .ConfigureAwait(false); + var checkpoint = await containerProvider + .GetIndexContainer() + .ReadItemAsync>( + name, + new PartitionKey(streamId.Value), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - return new Checkpoint( - checkpoint.Resource.Name, - checkpoint.Resource.StreamId, - checkpoint.Resource.StreamVersion, - checkpoint.Resource.Timestamp, - checkpoint.Resource.State); - } - catch (CosmosException ex) - when (ex.StatusCode == HttpStatusCode.NotFound) - { - return null; - } + return new Checkpoint( + checkpoint.Resource.Name, + checkpoint.Resource.StreamId, + checkpoint.Resource.StreamVersion, + checkpoint.Resource.Timestamp, + checkpoint.Resource.State); + } + catch (CosmosException ex) + when (ex.StatusCode == HttpStatusCode.NotFound) + { + return null; } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointWriter.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointWriter.cs index 9287cf4..fafb405 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointWriter.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosCheckpointWriter.cs @@ -1,41 +1,38 @@ -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos -{ - internal class CosmosCheckpointWriter : IStreamCheckpointWriter - { - private readonly IEventStoreContainerProvider containerProvider; - private readonly IDateTimeProvider dateTimeProvider; +namespace Atc.Cosmos.EventStore.Cosmos; - public CosmosCheckpointWriter( - IEventStoreContainerProvider containerProvider, - IDateTimeProvider dateTimeProvider) - { - this.containerProvider = containerProvider; - this.dateTimeProvider = dateTimeProvider; - } +internal class CosmosCheckpointWriter : IStreamCheckpointWriter +{ + private readonly IEventStoreContainerProvider containerProvider; + private readonly IDateTimeProvider dateTimeProvider; - public Task WriteAsync( - string name, - StreamId streamId, - StreamVersion streamVersion, - object? state, - CancellationToken cancellationToken) - => containerProvider - .GetIndexContainer() - .UpsertItemAsync( - new CheckpointDocument( - name, - streamId, - streamVersion, - dateTimeProvider.GetDateTime(), - state), - new PartitionKey(streamId.Value), - new ItemRequestOptions { EnableContentResponseOnWrite = false }, - cancellationToken); + public CosmosCheckpointWriter( + IEventStoreContainerProvider containerProvider, + IDateTimeProvider dateTimeProvider) + { + this.containerProvider = containerProvider; + this.dateTimeProvider = dateTimeProvider; } + + public Task WriteAsync( + string name, + StreamId streamId, + StreamVersion streamVersion, + object? state, + CancellationToken cancellationToken) + => containerProvider + .GetIndexContainer() + .UpsertItemAsync( + new CheckpointDocument( + name, + streamId, + streamVersion, + dateTimeProvider.GetDateTime(), + state), + new PartitionKey(streamId.Value), + new ItemRequestOptions { EnableContentResponseOnWrite = false }, + cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs index f3e012c..267bfb5 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs @@ -1,56 +1,54 @@ -using System; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Options; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal sealed class CosmosClientFactory : ICosmosClientFactory, IDisposable { - internal sealed class CosmosClientFactory : ICosmosClientFactory, IDisposable - { - private readonly CosmosClient cosmosClient; - private bool disposedValue; + private readonly CosmosClient cosmosClient; + private bool disposedValue; - public CosmosClientFactory( - IOptions options, - CosmosEventSerializer eventSerializer) - { - options.Value.CosmosClientOptions.Serializer = eventSerializer; + public CosmosClientFactory( + IOptions options, + CosmosEventSerializer eventSerializer) + { + options.Value.CosmosClientOptions.Serializer = eventSerializer; #pragma warning disable CS0618 // Type or member is obsolete - cosmosClient = options.Value.Credential is null - ? options.Value.ConnectionString is not null - ? new CosmosClient( - options.Value.ConnectionString, - options.Value.CosmosClientOptions) - : new CosmosClient( - options.Value.Endpoint, - options.Value.AuthKey, - options.Value.CosmosClientOptions) + cosmosClient = options.Value.Credential is null + ? options.Value.ConnectionString is not null + ? new CosmosClient( + options.Value.ConnectionString, + options.Value.CosmosClientOptions) : new CosmosClient( options.Value.Endpoint, - options.Value.Credential, - options.Value.CosmosClientOptions); + options.Value.AuthKey, + options.Value.CosmosClientOptions) + : new CosmosClient( + options.Value.Endpoint, + options.Value.Credential, + options.Value.CosmosClientOptions); #pragma warning restore CS0618 // Type or member is obsolete - } + } - public CosmosClient GetClient() - => cosmosClient; + public CosmosClient GetClient() + => cosmosClient; - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } - private void Dispose(bool disposing) + private void Dispose(bool disposing) + { + if (!disposedValue) { - if (!disposedValue) + if (disposing) { - if (disposing) - { - cosmosClient.Dispose(); - } - - disposedValue = true; + cosmosClient.Dispose(); } + + disposedValue = true; } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosContainerProvider.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosContainerProvider.cs index bd238ce..0fef945 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosContainerProvider.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosContainerProvider.cs @@ -1,46 +1,45 @@ using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Options; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosContainerProvider : IEventStoreContainerProvider { - internal class CosmosContainerProvider : IEventStoreContainerProvider - { - private readonly ICosmosClientFactory factory; - private readonly string databaseId; - private readonly string containerId; - private readonly string indexId; - private readonly string subscriptionId; + private readonly ICosmosClientFactory factory; + private readonly string databaseId; + private readonly string containerId; + private readonly string indexId; + private readonly string subscriptionId; - public CosmosContainerProvider( - ICosmosClientFactory cosmosClientFactory, - IOptions options) - { - factory = cosmosClientFactory; - databaseId = options.Value.EventStoreDatabaseId; - containerId = options.Value.EventStoreContainerId; - indexId = options.Value.IndexContainerId; - subscriptionId = options.Value.SubscriptionContainerId; - } + public CosmosContainerProvider( + ICosmosClientFactory cosmosClientFactory, + IOptions options) + { + factory = cosmosClientFactory; + databaseId = options.Value.EventStoreDatabaseId; + containerId = options.Value.EventStoreContainerId; + indexId = options.Value.IndexContainerId; + subscriptionId = options.Value.SubscriptionContainerId; + } - public Container GetStreamContainer() - => factory - .GetClient() - .GetContainer( - databaseId, - containerId); + public Container GetStreamContainer() + => factory + .GetClient() + .GetContainer( + databaseId, + containerId); - public Container GetIndexContainer() - => factory - .GetClient() - .GetContainer( - databaseId, - indexId); + public Container GetIndexContainer() + => factory + .GetClient() + .GetContainer( + databaseId, + indexId); - public Container GetSubscriptionContainer() - => factory - .GetClient() - .GetContainer( - databaseId, - subscriptionId); - } + public Container GetSubscriptionContainer() + => factory + .GetClient() + .GetContainer( + databaseId, + subscriptionId); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs index 7f8eb73..9d66ea6 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventSerializer.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.Json; @@ -8,96 +7,95 @@ using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Options; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +/// +/// EventStore cosmos JSON serializer implementation for . +/// +internal class CosmosEventSerializer : CosmosSerializer { - /// - /// EventStore cosmos JSON serializer implementation for . - /// - internal class CosmosEventSerializer : CosmosSerializer - { - private readonly JsonSerializerOptions jsonSerializerOptions; + private readonly JsonSerializerOptions jsonSerializerOptions; - public CosmosEventSerializer( - IOptions options, - IEventTypeProvider typeProvider) + public CosmosEventSerializer( + IOptions options, + IEventTypeProvider typeProvider) + { + jsonSerializerOptions = new JsonSerializerOptions { - jsonSerializerOptions = new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - WriteIndented = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }; + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; - jsonSerializerOptions.Converters.Add(new TimeSpanConverter()); - jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); - jsonSerializerOptions.Converters.Add(new StreamIdConverter()); - jsonSerializerOptions.Converters.Add(new StreamVersionConverter()); - jsonSerializerOptions.Converters.Add( - new EventDocumentConverter( - new EventDataConverterPipelineBuilder() - .AddConverter(new FaultedEventDataConverter()) - .AddConverter(new UnknownEventDataConverter()) - .AddConverters(options.Value.EventDataConverter) - .AddConverter(new NamedEventConverter(typeProvider)) - .Build())); + jsonSerializerOptions.Converters.Add(new TimeSpanConverter()); + jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + jsonSerializerOptions.Converters.Add(new StreamIdConverter()); + jsonSerializerOptions.Converters.Add(new StreamVersionConverter()); + jsonSerializerOptions.Converters.Add( + new EventDocumentConverter( + new EventDataConverterPipelineBuilder() + .AddConverter(new FaultedEventDataConverter()) + .AddConverter(new UnknownEventDataConverter()) + .AddConverters(options.Value.EventDataConverter) + .AddConverter(new NamedEventConverter(typeProvider)) + .Build())); - foreach (var converter in options.Value.CustomJsonConverter) - { - jsonSerializerOptions.Converters.Add(converter); - } + foreach (var converter in options.Value.CustomJsonConverter) + { + jsonSerializerOptions.Converters.Add(converter); + } + } + + [return: MaybeNull] + public override T FromStream(Stream stream) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); } - [return: MaybeNull] - public override T FromStream(Stream stream) + using (stream) { - if (stream is null) + if (stream.CanSeek && stream.Length == 0) { - throw new ArgumentNullException(nameof(stream)); + return default; } - using (stream) + if (typeof(Stream).IsAssignableFrom(typeof(T))) { - if (stream.CanSeek && stream.Length == 0) - { - return default; - } - - if (typeof(Stream).IsAssignableFrom(typeof(T))) - { - return (T)(object)stream; - } - - // Response data from cosmos always comes as a memory stream. - // Note: This might change in v4, but so far it doesn't look like it. - if (stream is MemoryStream memoryStream && memoryStream.TryGetBuffer(out ArraySegment buffer)) - { - return JsonSerializer.Deserialize(buffer, jsonSerializerOptions); - } + return (T)(object)stream; + } - return default; + // Response data from cosmos always comes as a memory stream. + // Note: This might change in v4, but so far it doesn't look like it. + if (stream is MemoryStream memoryStream && memoryStream.TryGetBuffer(out ArraySegment buffer)) + { + return JsonSerializer.Deserialize(buffer, jsonSerializerOptions); } + + return default; } + } - public override Stream ToStream(T input) + public override Stream ToStream(T input) + { + if (input is null) { - if (input is null) - { - throw new ArgumentNullException(nameof(input)); - } + throw new ArgumentNullException(nameof(input)); + } - var streamPayload = new MemoryStream(); + var streamPayload = new MemoryStream(); - using var utf8JsonWriter = new Utf8JsonWriter( - streamPayload, - new JsonWriterOptions - { - Indented = jsonSerializerOptions.WriteIndented, - }); + using var utf8JsonWriter = new Utf8JsonWriter( + streamPayload, + new JsonWriterOptions + { + Indented = jsonSerializerOptions.WriteIndented, + }); - JsonSerializer.Serialize(utf8JsonWriter, input, jsonSerializerOptions); - streamPayload.Position = 0; + JsonSerializer.Serialize(utf8JsonWriter, input, jsonSerializerOptions); + streamPayload.Position = 0; - return streamPayload; - } + return streamPayload; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventStoreInitializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventStoreInitializer.cs index 751f772..0cf9686 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventStoreInitializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosEventStoreInitializer.cs @@ -1,129 +1,125 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Options; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosEventStoreInitializer : IEventStoreInitializer { - internal class CosmosEventStoreInitializer : IEventStoreInitializer + private readonly EventStoreClientOptions options; + private readonly ICosmosClientFactory clientFactory; + + public CosmosEventStoreInitializer( + IOptions options, + ICosmosClientFactory clientFactory) { - private readonly EventStoreClientOptions options; - private readonly ICosmosClientFactory clientFactory; + this.options = options.Value; + this.clientFactory = clientFactory; + } - public CosmosEventStoreInitializer( - IOptions options, - ICosmosClientFactory clientFactory) - { - this.options = options.Value; - this.clientFactory = clientFactory; - } + public void CreateEventStore(ThroughputProperties throughputProperties) + { + CreateEventStoreAsync(throughputProperties, CancellationToken.None) + .GetAwaiter() + .GetResult(); + } - public void CreateEventStore(ThroughputProperties throughputProperties) - { - CreateEventStoreAsync(throughputProperties, CancellationToken.None) - .GetAwaiter() - .GetResult(); - } - - public async Task CreateEventStoreAsync( - ThroughputProperties throughputProperties, - CancellationToken cancellationToken) - { - await clientFactory - .GetClient() - .CreateDatabaseIfNotExistsAsync( - options.EventStoreDatabaseId, - throughputProperties, - cancellationToken: cancellationToken) - .ConfigureAwait(false); - - await CreateEventStoreContainerAsync(cancellationToken) - .ConfigureAwait(continueOnCapturedContext: false); - await CreateSubscriptionContainerAsync(cancellationToken) - .ConfigureAwait(continueOnCapturedContext: false); - await CreateIndexContainerAsync(cancellationToken) - .ConfigureAwait(continueOnCapturedContext: false); - } - - private Task CreateEventStoreContainerAsync(CancellationToken cancellationToken) + public async Task CreateEventStoreAsync( + ThroughputProperties throughputProperties, + CancellationToken cancellationToken) + { + await clientFactory + .GetClient() + .CreateDatabaseIfNotExistsAsync( + options.EventStoreDatabaseId, + throughputProperties, + cancellationToken: cancellationToken) + .ConfigureAwait(false); + + await CreateEventStoreContainerAsync(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + await CreateSubscriptionContainerAsync(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + await CreateIndexContainerAsync(cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + } + + private Task CreateEventStoreContainerAsync(CancellationToken cancellationToken) + { + var containerOptions = new ContainerProperties { - var containerOptions = new ContainerProperties + IndexingPolicy = new IndexingPolicy + { + Automatic = true, + IndexingMode = IndexingMode.Consistent, + }, + Id = options.EventStoreContainerId, + PartitionKeyPath = "/pk", + }; + + containerOptions.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" }); + + // Exclude event data from indexing. + containerOptions.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/data/*" }); + + containerOptions.IndexingPolicy.CompositeIndexes.Add(new( + new[] { - IndexingPolicy = new IndexingPolicy - { - Automatic = true, - IndexingMode = IndexingMode.Consistent, - }, - Id = options.EventStoreContainerId, - PartitionKeyPath = "/pk", - }; - - containerOptions.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" }); - - // Exclude event data from indexing. - containerOptions.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/data/*" }); - - containerOptions.IndexingPolicy.CompositeIndexes.Add(new( - new[] - { - new CompositePath { Path = "/pk", Order = CompositePathSortOrder.Ascending }, - new CompositePath { Path = "/properties/version", Order = CompositePathSortOrder.Ascending }, - }.ToList())); - - return clientFactory - .GetClient() - .GetDatabase(options.EventStoreDatabaseId) - .CreateContainerIfNotExistsAsync( - containerOptions, - cancellationToken: cancellationToken); - } - - private Task CreateSubscriptionContainerAsync(CancellationToken cancellationToken) + new CompositePath { Path = "/pk", Order = CompositePathSortOrder.Ascending }, + new CompositePath { Path = "/properties/version", Order = CompositePathSortOrder.Ascending }, + }.ToList())); + + return clientFactory + .GetClient() + .GetDatabase(options.EventStoreDatabaseId) + .CreateContainerIfNotExistsAsync( + containerOptions, + cancellationToken: cancellationToken); + } + + private Task CreateSubscriptionContainerAsync(CancellationToken cancellationToken) + { + var containerOptions = new ContainerProperties { - var containerOptions = new ContainerProperties + IndexingPolicy = new IndexingPolicy { - IndexingPolicy = new IndexingPolicy - { - Automatic = true, - IndexingMode = IndexingMode.Consistent, - }, - Id = options.SubscriptionContainerId, - PartitionKeyPath = "/id", - }; - - return clientFactory - .GetClient() - .GetDatabase(options.EventStoreDatabaseId) - .CreateContainerIfNotExistsAsync( - containerOptions, - cancellationToken: cancellationToken); - } - - private Task CreateIndexContainerAsync(CancellationToken cancellationToken) + Automatic = true, + IndexingMode = IndexingMode.Consistent, + }, + Id = options.SubscriptionContainerId, + PartitionKeyPath = "/id", + }; + + return clientFactory + .GetClient() + .GetDatabase(options.EventStoreDatabaseId) + .CreateContainerIfNotExistsAsync( + containerOptions, + cancellationToken: cancellationToken); + } + + private Task CreateIndexContainerAsync(CancellationToken cancellationToken) + { + var containerOptions = new ContainerProperties { - var containerOptions = new ContainerProperties + IndexingPolicy = new IndexingPolicy { - IndexingPolicy = new IndexingPolicy - { - Automatic = true, - IndexingMode = IndexingMode.Consistent, - }, - Id = options.IndexContainerId, - PartitionKeyPath = "/pk", - }; - - containerOptions.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" }); - - // Exclude snapshot data from indexing. - containerOptions.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/data/*" }); - - return clientFactory - .GetClient() - .GetDatabase(options.EventStoreDatabaseId) - .CreateContainerIfNotExistsAsync( - containerOptions, - cancellationToken: cancellationToken); - } + Automatic = true, + IndexingMode = IndexingMode.Consistent, + }, + Id = options.IndexContainerId, + PartitionKeyPath = "/pk", + }; + + containerOptions.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" }); + + // Exclude snapshot data from indexing. + containerOptions.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/data/*" }); + + return clientFactory + .GetClient() + .GetDatabase(options.EventStoreDatabaseId) + .CreateContainerIfNotExistsAsync( + containerOptions, + cancellationToken: cancellationToken); } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosMetadataReader.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosMetadataReader.cs index b6bbb17..9943001 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosMetadataReader.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosMetadataReader.cs @@ -1,54 +1,51 @@ using System.Net; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosMetadataReader : IStreamMetadataReader { - internal class CosmosMetadataReader : IStreamMetadataReader - { - private readonly IEventStoreContainerProvider containerProvider; - private readonly IDateTimeProvider timeProvider; + private readonly IEventStoreContainerProvider containerProvider; + private readonly IDateTimeProvider timeProvider; - public CosmosMetadataReader( - IEventStoreContainerProvider containerProvider, - IDateTimeProvider timeProvider) - { - this.containerProvider = containerProvider; - this.timeProvider = timeProvider; - } + public CosmosMetadataReader( + IEventStoreContainerProvider containerProvider, + IDateTimeProvider timeProvider) + { + this.containerProvider = containerProvider; + this.timeProvider = timeProvider; + } - public async Task GetAsync( - StreamId streamId, - CancellationToken cancellationToken) + public async Task GetAsync( + StreamId streamId, + CancellationToken cancellationToken) + { + try { - try - { - var metadata = await containerProvider - .GetStreamContainer() - .ReadItemAsync( - StreamMetadata.StreamMetadataId, - new PartitionKey(streamId.Value), - cancellationToken: cancellationToken) - .ConfigureAwait(false); + var metadata = await containerProvider + .GetStreamContainer() + .ReadItemAsync( + StreamMetadata.StreamMetadataId, + new PartitionKey(streamId.Value), + cancellationToken: cancellationToken) + .ConfigureAwait(false); - metadata.Resource.ETag = metadata.ETag; + metadata.Resource.ETag = metadata.ETag; - return metadata.Resource; - } - catch (CosmosException ex) - when (ex.StatusCode == HttpStatusCode.NotFound) - { - return new StreamMetadata( - StreamMetadata.StreamMetadataId, - streamId.Value, - streamId, - StreamVersion.StartOfStream, - StreamState.New, - timeProvider.GetDateTime()); - } + return metadata.Resource; + } + catch (CosmosException ex) + when (ex.StatusCode == HttpStatusCode.NotFound) + { + return new StreamMetadata( + StreamMetadata.StreamMetadataId, + streamId.Value, + streamId, + StreamVersion.StartOfStream, + StreamState.New, + timeProvider.GetDateTime()); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIndexReader.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIndexReader.cs index f97f76c..3d54b9f 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIndexReader.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIndexReader.cs @@ -1,70 +1,66 @@ -using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Threading; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosStreamIndexReader : IStreamIndexReader { - internal class CosmosStreamIndexReader : IStreamIndexReader - { - private readonly IEventStoreContainerProvider containerProvider; + private readonly IEventStoreContainerProvider containerProvider; - public CosmosStreamIndexReader( - IEventStoreContainerProvider containerProvider) - => this.containerProvider = containerProvider; + public CosmosStreamIndexReader( + IEventStoreContainerProvider containerProvider) + => this.containerProvider = containerProvider; + + public async IAsyncEnumerable ReadAsync( + string? filter, + DateTimeOffset? createdAfter, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + var pk = new PartitionKey(nameof(StreamIndex)); + var resultSet = containerProvider + .GetIndexContainer() + .GetItemQueryIterator( + GetQuery(filter, createdAfter), + requestOptions: GetRequestOptions(pk)); - public async IAsyncEnumerable ReadAsync( - string? filter, - DateTimeOffset? createdAfter, - [EnumeratorCancellation] CancellationToken cancellationToken) + while (resultSet.HasMoreResults && !cancellationToken.IsCancellationRequested) { - var pk = new PartitionKey(nameof(StreamIndex)); - var resultSet = containerProvider - .GetIndexContainer() - .GetItemQueryIterator( - GetQuery(filter, createdAfter), - requestOptions: GetRequestOptions(pk)); + var items = await resultSet + .ReadNextAsync(cancellationToken) + .ConfigureAwait(false); - while (resultSet.HasMoreResults && !cancellationToken.IsCancellationRequested) + foreach (var item in items.Resource) { - var items = await resultSet - .ReadNextAsync(cancellationToken) - .ConfigureAwait(false); - - foreach (var item in items.Resource) - { - yield return item; - } + yield return item; } } + } - private static QueryRequestOptions GetRequestOptions(PartitionKey partitionKey) - => new() { PartitionKey = partitionKey, }; + private static QueryRequestOptions GetRequestOptions(PartitionKey partitionKey) + => new() { PartitionKey = partitionKey, }; - private static QueryDefinition GetQuery( - string? filter, - DateTimeOffset? createdAfter) - => filter is null - ? GetAllQuery(createdAfter) - : GetFilterQuery(filter, createdAfter); + private static QueryDefinition GetQuery( + string? filter, + DateTimeOffset? createdAfter) + => filter is null + ? GetAllQuery(createdAfter) + : GetFilterQuery(filter, createdAfter); - private static QueryDefinition GetAllQuery(DateTimeOffset? createdAfter) - => createdAfter is not null - ? new QueryDefinition( - "SELECT * FROM c WHERE c.timestamp > @createdAfter") - .WithParameter("@createdAfter", createdAfter) - : new QueryDefinition("SELECT * FROM c"); + private static QueryDefinition GetAllQuery(DateTimeOffset? createdAfter) + => createdAfter is not null + ? new QueryDefinition( + "SELECT * FROM c WHERE c.timestamp > @createdAfter") + .WithParameter("@createdAfter", createdAfter) + : new QueryDefinition("SELECT * FROM c"); - private static QueryDefinition GetFilterQuery(string filter, DateTimeOffset? createdAfter) - => createdAfter is not null - ? new QueryDefinition( - "SELECT * FROM c WHERE c.id LIKE @filter AND c.timestamp > @createdAfter") - .WithParameter("@filter", filter) - .WithParameter("@createdAfter", createdAfter) - : new QueryDefinition( - "SELECT * FROM c WHERE c.id LIKE @filter") - .WithParameter("@filter", filter); - } + private static QueryDefinition GetFilterQuery(string filter, DateTimeOffset? createdAfter) + => createdAfter is not null + ? new QueryDefinition( + "SELECT * FROM c WHERE c.id LIKE @filter AND c.timestamp > @createdAfter") + .WithParameter("@filter", filter) + .WithParameter("@createdAfter", createdAfter) + : new QueryDefinition( + "SELECT * FROM c WHERE c.id LIKE @filter") + .WithParameter("@filter", filter); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIterator.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIterator.cs index a96848e..6acf043 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIterator.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamIterator.cs @@ -1,44 +1,40 @@ -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosStreamIterator : IStreamIterator { - internal class CosmosStreamIterator : IStreamIterator - { - private readonly IEventStoreContainerProvider containerProvider; + private readonly IEventStoreContainerProvider containerProvider; - public CosmosStreamIterator( - IEventStoreContainerProvider containerProvider) - => this.containerProvider = containerProvider; + public CosmosStreamIterator( + IEventStoreContainerProvider containerProvider) + => this.containerProvider = containerProvider; - public async IAsyncEnumerable ReadAsync( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter, - [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable ReadAsync( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + var pk = new PartitionKey(streamId.Value); + var resultSet = containerProvider + .GetStreamContainer() + .GetItemQueryIterator( + CosmosStreamQueryBuilder.GetQueryDefinition(streamId, fromVersion, filter), + requestOptions: new() { PartitionKey = pk }); + + while (resultSet.HasMoreResults && !cancellationToken.IsCancellationRequested) { - var pk = new PartitionKey(streamId.Value); - var resultSet = containerProvider - .GetStreamContainer() - .GetItemQueryIterator( - CosmosStreamQueryBuilder.GetQueryDefinition(streamId, fromVersion, filter), - requestOptions: new() { PartitionKey = pk }); + var items = await resultSet + .ReadNextAsync(cancellationToken) + .ConfigureAwait(false); - while (resultSet.HasMoreResults && !cancellationToken.IsCancellationRequested) + foreach (var item in items.Resource.Where(d => d is not null)) { - var items = await resultSet - .ReadNextAsync(cancellationToken) - .ConfigureAwait(false); - - foreach (var item in items.Resource.Where(d => d is not null)) - { - yield return item; - } + yield return item; } } } diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamQueryBuilder.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamQueryBuilder.cs index dcbf210..b5f947c 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamQueryBuilder.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosStreamQueryBuilder.cs @@ -1,54 +1,51 @@ -using System.Collections.Generic; -using System.Linq; using System.Text; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +public static class CosmosStreamQueryBuilder { - public static class CosmosStreamQueryBuilder + internal static QueryDefinition GetQueryDefinition( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter) { - internal static QueryDefinition GetQueryDefinition( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter) + var parameters = new Dictionary(); + var query = new StringBuilder(); + query.Append("SELECT * FROM e WHERE e.pk = @partitionKey "); + parameters["@partitionKey"] = streamId.Value; + + if (fromVersion != StreamVersion.Any && fromVersion != StreamVersion.NotEmpty) + { + query.Append("AND e.properties.version >= @fromVersion "); + parameters["@fromVersion"] = fromVersion.Value; + } + + if (filter?.IncludeEvents is not null && filter.IncludeEvents.Any()) { - var parameters = new Dictionary(); - var query = new StringBuilder(); - query.Append("SELECT * FROM e WHERE e.pk = @partitionKey "); - parameters["@partitionKey"] = streamId.Value; - - if (fromVersion != StreamVersion.Any && fromVersion != StreamVersion.NotEmpty) - { - query.Append("AND e.properties.version >= @fromVersion "); - parameters["@fromVersion"] = fromVersion.Value; - } - - if (filter?.IncludeEvents is not null && filter.IncludeEvents.Any()) - { - var index = 1; - query.Append("AND ("); - query.AppendJoin(" OR ", filter.IncludeEvents.Select(name => GetFilterExpression(name, $"@name{index++}", parameters))); - query.Append(") "); - } - - query.Append("ORDER BY e.properties.version"); - - var definition = new QueryDefinition(query.ToString()); - foreach (var parameter in parameters) - { - definition.WithParameter(parameter.Key, parameter.Value); - } - - return definition; + var index = 1; + query.Append("AND ("); + query.AppendJoin(" OR ", filter.IncludeEvents.Select(name => GetFilterExpression(name, $"@name{index++}", parameters))); + query.Append(") "); } - private static string GetFilterExpression( - EventName name, - string parameterName, - Dictionary parameters) + query.Append("ORDER BY e.properties.version"); + + var definition = new QueryDefinition(query.ToString()); + foreach (var parameter in parameters) { - parameters[parameterName] = name; - return $"e.properties.name = {parameterName}"; + definition.WithParameter(parameter.Key, parameter.Value); } + + return definition; + } + + private static string GetFilterExpression( + EventName name, + string parameterName, + Dictionary parameters) + { + parameters[parameterName] = name; + return $"e.properties.name = {parameterName}"; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionFactory.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionFactory.cs index b6c3d30..669f293 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionFactory.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionFactory.cs @@ -1,62 +1,59 @@ -using System; -using System.Linq; using Atc.Cosmos.EventStore.Diagnostics; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosSubscriptionFactory : IStreamSubscriptionFactory { - internal class CosmosSubscriptionFactory : IStreamSubscriptionFactory + private readonly IEventStoreContainerProvider containerProvider; + private readonly ISubscriptionTelemetry telemetry; + + public CosmosSubscriptionFactory( + IEventStoreContainerProvider containerProvider, + ISubscriptionTelemetry telemetry) { - private readonly IEventStoreContainerProvider containerProvider; - private readonly ISubscriptionTelemetry telemetry; + this.containerProvider = containerProvider; + this.telemetry = telemetry; + } - public CosmosSubscriptionFactory( - IEventStoreContainerProvider containerProvider, - ISubscriptionTelemetry telemetry) + public IStreamSubscription Create( + ConsumerGroup consumerGroup, + SubscriptionStartOptions startOptions, + ProcessEventsHandler eventsHandler, + ProcessExceptionHandler exceptionHandler) + { + var builder = containerProvider + .GetStreamContainer() + .GetChangeFeedProcessorBuilder( + GetProcessorName(consumerGroup), + (c, ct) => eventsHandler(c.Where(ExcludeMetaDataChanges).ToArray(), ct)) + .WithErrorNotification((lt, ex) => exceptionHandler(lt, ex)) + .WithLeaseContainer(containerProvider.GetSubscriptionContainer()) + .WithMaxItems(100) + .WithPollInterval(TimeSpan.FromMilliseconds(1000)); + + if (startOptions == SubscriptionStartOptions.FromBegining) { - this.containerProvider = containerProvider; - this.telemetry = telemetry; + // Instruct processor to start from beginning. + // see https://docs.microsoft.com/en-us/azure/cosmos-db/change-feed-processor#reading-from-the-beginning + builder.WithStartTime(DateTime.MinValue.ToUniversalTime()); } - public IStreamSubscription Create( - ConsumerGroup consumerGroup, - SubscriptionStartOptions startOptions, - ProcessEventsHandler eventsHandler, - ProcessExceptionHandler exceptionHandler) + if (!string.IsNullOrEmpty(consumerGroup.Instance)) { - var builder = containerProvider - .GetStreamContainer() - .GetChangeFeedProcessorBuilder( - GetProcessorName(consumerGroup), - (c, ct) => eventsHandler(c.Where(ExcludeMetaDataChanges).ToArray(), ct)) - .WithErrorNotification((lt, ex) => exceptionHandler(lt, ex)) - .WithLeaseContainer(containerProvider.GetSubscriptionContainer()) - .WithMaxItems(100) - .WithPollInterval(TimeSpan.FromMilliseconds(1000)); - - if (startOptions == SubscriptionStartOptions.FromBegining) - { - // Instruct processor to start from beginning. - // see https://docs.microsoft.com/en-us/azure/cosmos-db/change-feed-processor#reading-from-the-beginning - builder.WithStartTime(DateTime.MinValue.ToUniversalTime()); - } - - if (!string.IsNullOrEmpty(consumerGroup.Instance)) - { - builder.WithInstanceName(consumerGroup.Instance); - } - - return new CosmosSubscriptionProcessor( - telemetry, - builder.Build(), - consumerGroup); + builder.WithInstanceName(consumerGroup.Instance); } - private static bool ExcludeMetaDataChanges(EventDocument doc) - => doc is not null; - - private static string GetProcessorName(ConsumerGroup consumerGroup) - => consumerGroup.Name + ":"; + return new CosmosSubscriptionProcessor( + telemetry, + builder.Build(), + consumerGroup); } + + private static bool ExcludeMetaDataChanges(EventDocument doc) + => doc is not null; + + private static string GetProcessorName(ConsumerGroup consumerGroup) + => consumerGroup.Name + ":"; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionProcessor.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionProcessor.cs index 925c77b..a615a04 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionProcessor.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionProcessor.cs @@ -1,47 +1,45 @@ -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Diagnostics; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosSubscriptionProcessor : IStreamSubscription { - internal class CosmosSubscriptionProcessor : IStreamSubscription + private readonly ISubscriptionTelemetry telemetry; + private readonly ChangeFeedProcessor processor; + private readonly ConsumerGroup consumerGroup; + private ISubscriptionActivity? activity; + + public CosmosSubscriptionProcessor( + ISubscriptionTelemetry telemetry, + ChangeFeedProcessor processor, + ConsumerGroup consumerGroup) { - private readonly ISubscriptionTelemetry telemetry; - private readonly ChangeFeedProcessor processor; - private readonly ConsumerGroup consumerGroup; - private ISubscriptionActivity? activity; - - public CosmosSubscriptionProcessor( - ISubscriptionTelemetry telemetry, - ChangeFeedProcessor processor, - ConsumerGroup consumerGroup) - { - this.telemetry = telemetry; - this.processor = processor; - this.consumerGroup = consumerGroup; - } + this.telemetry = telemetry; + this.processor = processor; + this.consumerGroup = consumerGroup; + } + + public async Task StartAsync() + { + await processor + .StartAsync() + .ConfigureAwait(false); - public async Task StartAsync() + activity = telemetry.SubscriptionStarted(consumerGroup); + } + + public async Task StopAsync() + { + if (activity is { }) { await processor - .StartAsync() + .StopAsync() .ConfigureAwait(false); - activity = telemetry.SubscriptionStarted(consumerGroup); + activity.SubscriptionStopped(); } - public async Task StopAsync() - { - if (activity is { }) - { - await processor - .StopAsync() - .ConfigureAwait(false); - - activity.SubscriptionStopped(); - } - - activity = null; - } + activity = null; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionRemover.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionRemover.cs index e323054..b7bee52 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionRemover.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosSubscriptionRemover.cs @@ -1,53 +1,50 @@ using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class CosmosSubscriptionRemover : IStreamSubscriptionRemover { - internal class CosmosSubscriptionRemover : IStreamSubscriptionRemover + private readonly IEventStoreContainerProvider containerProvider; + + public CosmosSubscriptionRemover(IEventStoreContainerProvider containerProvider) { - private readonly IEventStoreContainerProvider containerProvider; + this.containerProvider = containerProvider; + } - public CosmosSubscriptionRemover(IEventStoreContainerProvider containerProvider) - { - this.containerProvider = containerProvider; - } + public async Task DeleteAsync(ConsumerGroup consumerGroup, CancellationToken cancellationToken) + { + var resultSet = containerProvider + .GetSubscriptionContainer() + .GetItemQueryIterator( + new QueryDefinition("SELECT r.id FROM r WHERE STARTSWITH(r.id, @name, false)") + .WithParameter("@name", GetProcessorName(consumerGroup))); - public async Task DeleteAsync(ConsumerGroup consumerGroup, CancellationToken cancellationToken) + while (resultSet.HasMoreResults) { - var resultSet = containerProvider - .GetSubscriptionContainer() - .GetItemQueryIterator( - new QueryDefinition("SELECT r.id FROM r WHERE STARTSWITH(r.id, @name, false)") - .WithParameter("@name", GetProcessorName(consumerGroup))); - - while (resultSet.HasMoreResults) + var registrations = await resultSet + .ReadNextAsync(cancellationToken) + .ConfigureAwait(false); + foreach (var registration in registrations) { - var registrations = await resultSet - .ReadNextAsync(cancellationToken) + await containerProvider + .GetSubscriptionContainer() + .DeleteItemAsync( + registration.Id, + new PartitionKey(registration.Id), + cancellationToken: cancellationToken) .ConfigureAwait(false); - foreach (var registration in registrations) - { - await containerProvider - .GetSubscriptionContainer() - .DeleteItemAsync( - registration.Id, - new PartitionKey(registration.Id), - cancellationToken: cancellationToken) - .ConfigureAwait(false); - } } } + } - private static string GetProcessorName(ConsumerGroup consumerGroup) - => consumerGroup.Name + ":"; + private static string GetProcessorName(ConsumerGroup consumerGroup) + => consumerGroup.Name + ":"; - internal class SubscriptionLease - { - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - } + internal class SubscriptionLease + { + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs b/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs index 69b600b..c3be955 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs @@ -1,11 +1,9 @@ -using System; using Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal class GuidEventIdProvider : IEventIdProvider { - internal class GuidEventIdProvider : IEventIdProvider - { - public string CreateUniqueId(IStreamMetadata metadata) - => Guid.NewGuid().ToString(); - } + public string CreateUniqueId(IStreamMetadata metadata) + => Guid.NewGuid().ToString(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/ICosmosClientFactory.cs b/src/Atc.Cosmos.EventStore/Cosmos/ICosmosClientFactory.cs index 8a3f04d..8ce9e95 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/ICosmosClientFactory.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/ICosmosClientFactory.cs @@ -1,9 +1,8 @@ using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal interface ICosmosClientFactory { - internal interface ICosmosClientFactory - { - CosmosClient GetClient(); - } + CosmosClient GetClient(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreContainerProvider.cs b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreContainerProvider.cs index c041ffe..e133e4c 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreContainerProvider.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreContainerProvider.cs @@ -1,13 +1,12 @@ using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.Cosmos +namespace Atc.Cosmos.EventStore.Cosmos; + +internal interface IEventStoreContainerProvider { - internal interface IEventStoreContainerProvider - { - Container GetStreamContainer(); + Container GetStreamContainer(); - Container GetSubscriptionContainer(); + Container GetSubscriptionContainer(); - Container GetIndexContainer(); - } + Container GetIndexContainer(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs index ebbd6a2..c2a9105 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/IEventStoreInitializer.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.Azure.Cosmos; namespace Atc.Cosmos.EventStore.Cosmos; diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/EventCatalogBuilder.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/EventCatalogBuilder.cs index 099245d..edb554b 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/EventCatalogBuilder.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/EventCatalogBuilder.cs @@ -1,22 +1,19 @@ -using System; -using System.Collections.Generic; using Atc.Cosmos.EventStore.Events; using Microsoft.Extensions.DependencyInjection; -namespace Atc.Cosmos.EventStore.DependencyInjection -{ - internal class EventCatalogBuilder : IEventCatalogBuilder - { - private readonly Dictionary mappings = new(); +namespace Atc.Cosmos.EventStore.DependencyInjection; - public IEventCatalogBuilder FromType(string name, Type type) - { - mappings.Add(name, type); +internal class EventCatalogBuilder : IEventCatalogBuilder +{ + private readonly Dictionary mappings = new(); - return this; - } + public IEventCatalogBuilder FromType(string name, Type type) + { + mappings.Add(name, type); - public IEventCatalog Build() - => new EventCatalog(mappings); + return this; } + + public IEventCatalog Build() + => new EventCatalog(mappings); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs index d80a00f..c92ece8 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs @@ -1,4 +1,3 @@ -using System; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.InMemory; @@ -6,90 +5,89 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Atc.Cosmos.EventStore.DependencyInjection -{ - public sealed class EventStoreOptionsBuilder - { - internal EventStoreOptionsBuilder(IServiceCollection services) - => Services = services; +namespace Atc.Cosmos.EventStore.DependencyInjection; - public IServiceCollection Services { get; } - - /// - /// Configure event store with default options. - /// - /// Option builder. - public EventStoreOptionsBuilder UseCosmosDb() - => UseCosmosDb(o => { }); +public sealed class EventStoreOptionsBuilder +{ + internal EventStoreOptionsBuilder(IServiceCollection services) + => Services = services; - public EventStoreOptionsBuilder UseCosmosDb( - Action configure) - { - Services.Configure(configure); + public IServiceCollection Services { get; } - Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); + /// + /// Configure event store with default options. + /// + /// Option builder. + public EventStoreOptionsBuilder UseCosmosDb() + => UseCosmosDb(o => { }); - Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); + public EventStoreOptionsBuilder UseCosmosDb( + Action configure) + { + Services.Configure(configure); - Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); - Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); - return this; - } + Services.TryAddSingleton(); + Services.TryAddSingleton(); - public EventStoreOptionsBuilder UseCustomDateTimeProvider() - where T : class, IDateTimeProvider - { - Services.TryAddSingleton(); + Services.TryAddSingleton(); + Services.TryAddSingleton(); - return this; - } + return this; + } - public EventStoreOptionsBuilder UseCustomEventIdProvider() - where T : class, IEventIdProvider - { - Services.TryAddSingleton(); + public EventStoreOptionsBuilder UseCustomDateTimeProvider() + where T : class, IDateTimeProvider + { + Services.TryAddSingleton(); - return this; - } + return this; + } - public EventStoreOptionsBuilder UseEvents(Action configure) - { - var builder = new EventCatalogBuilder(); + public EventStoreOptionsBuilder UseCustomEventIdProvider() + where T : class, IEventIdProvider + { + Services.TryAddSingleton(); - configure?.Invoke(builder); + return this; + } - var catalog = builder.Build(); + public EventStoreOptionsBuilder UseEvents(Action configure) + { + var builder = new EventCatalogBuilder(); - Services.TryAddSingleton(catalog); - Services.TryAddSingleton(catalog); + configure?.Invoke(builder); - return this; - } + var catalog = builder.Build(); - internal EventStoreOptionsBuilder UseInMemoryDb() - { - Services.TryAddSingleton(); + Services.TryAddSingleton(catalog); + Services.TryAddSingleton(catalog); - Services.TryAddSingleton(); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); - Services.TryAddSingleton(s => s.GetRequiredService()); + return this; + } - return this; - } + internal EventStoreOptionsBuilder UseInMemoryDb() + { + Services.TryAddSingleton(); + + Services.TryAddSingleton(); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + Services.TryAddSingleton(s => s.GetRequiredService()); + + return this; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/IEventCatalogBuilder.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/IEventCatalogBuilder.cs index 48498c3..329ce30 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/IEventCatalogBuilder.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/IEventCatalogBuilder.cs @@ -1,9 +1,6 @@ -using System; +namespace Microsoft.Extensions.DependencyInjection; -namespace Microsoft.Extensions.DependencyInjection +public interface IEventCatalogBuilder { - public interface IEventCatalogBuilder - { - IEventCatalogBuilder FromType(string name, Type type); - } + IEventCatalogBuilder FromType(string name, Type type); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs index f230d20..19400aa 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs @@ -1,4 +1,3 @@ -using System; using Atc.Cosmos.EventStore; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.DependencyInjection; @@ -7,35 +6,34 @@ using Atc.Cosmos.EventStore.Streams; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class ServiceCollectionExtensions { - public static class ServiceCollectionExtensions + public static IServiceCollection AddEventStore( + this IServiceCollection services, + Action configure) { - public static IServiceCollection AddEventStore( - this IServiceCollection services, - Action configure) - { - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); - var configureOptions = new EventStoreOptionsBuilder(services); - configure?.Invoke(configureOptions); + var configureOptions = new EventStoreOptionsBuilder(services); + configure?.Invoke(configureOptions); - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); - return services; - } + return services; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Diagnostics/EventStoreDiagnostics.cs b/src/Atc.Cosmos.EventStore/Diagnostics/EventStoreDiagnostics.cs index cdb09f4..46c3776 100644 --- a/src/Atc.Cosmos.EventStore/Diagnostics/EventStoreDiagnostics.cs +++ b/src/Atc.Cosmos.EventStore/Diagnostics/EventStoreDiagnostics.cs @@ -1,13 +1,12 @@ using System.Diagnostics; -namespace Atc.Cosmos.EventStore.Diagnostics +namespace Atc.Cosmos.EventStore.Diagnostics; + +internal static class EventStoreDiagnostics { - internal static class EventStoreDiagnostics - { - public static readonly DiagnosticSource SubscriptionListener - = new DiagnosticListener("Atc.Cosmos.EventStore.Subscription"); + public static readonly DiagnosticSource SubscriptionListener + = new DiagnosticListener("Atc.Cosmos.EventStore.Subscription"); - public static readonly DiagnosticSource StreamListener - = new DiagnosticListener("Atc.Cosmos.EventStore.Stream"); - } + public static readonly DiagnosticSource StreamListener + = new DiagnosticListener("Atc.Cosmos.EventStore.Stream"); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionActivity.cs b/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionActivity.cs index 10ddafe..df44444 100644 --- a/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionActivity.cs +++ b/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionActivity.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Diagnostics +namespace Atc.Cosmos.EventStore.Diagnostics; + +internal interface ISubscriptionActivity { - internal interface ISubscriptionActivity - { - void SubscriptionStopped(); - } + void SubscriptionStopped(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionTelemetry.cs b/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionTelemetry.cs index 2a705fa..d79c4c8 100644 --- a/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionTelemetry.cs +++ b/src/Atc.Cosmos.EventStore/Diagnostics/ISubscriptionTelemetry.cs @@ -1,11 +1,8 @@ -using System; +namespace Atc.Cosmos.EventStore.Diagnostics; -namespace Atc.Cosmos.EventStore.Diagnostics +internal interface ISubscriptionTelemetry { - internal interface ISubscriptionTelemetry - { - void ProcessExceptionHandlerFailed(Exception exception, ConsumerGroup consumerGroup); + void ProcessExceptionHandlerFailed(Exception exception, ConsumerGroup consumerGroup); - ISubscriptionActivity? SubscriptionStarted(ConsumerGroup consumerGroup); - } + ISubscriptionActivity? SubscriptionStarted(ConsumerGroup consumerGroup); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionActivity.cs b/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionActivity.cs index 6ec2143..20e6c39 100644 --- a/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionActivity.cs +++ b/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionActivity.cs @@ -1,31 +1,30 @@ using System.Diagnostics; -namespace Atc.Cosmos.EventStore.Diagnostics +namespace Atc.Cosmos.EventStore.Diagnostics; + +internal class SubscriptionActivity : ISubscriptionActivity { - internal class SubscriptionActivity : ISubscriptionActivity - { - private readonly DiagnosticSource source; - private readonly Activity activity; - private readonly ConsumerGroup consumerGroup; + private readonly DiagnosticSource source; + private readonly Activity activity; + private readonly ConsumerGroup consumerGroup; - public SubscriptionActivity( - DiagnosticSource source, - Activity activity, - ConsumerGroup consumerGroup) - { - this.source = source; - this.activity = activity; - this.consumerGroup = consumerGroup; - } + public SubscriptionActivity( + DiagnosticSource source, + Activity activity, + ConsumerGroup consumerGroup) + { + this.source = source; + this.activity = activity; + this.consumerGroup = consumerGroup; + } - public void SubscriptionStopped() + public void SubscriptionStopped() + { + if (source.IsEnabled(SubscriptionTelemetry.ActivityStartName)) { - if (source.IsEnabled(SubscriptionTelemetry.ActivityStartName)) - { - source.StopActivity( - activity, - new { ConsumerGroup = consumerGroup }); - } + source.StopActivity( + activity, + new { ConsumerGroup = consumerGroup }); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionTelemetry.cs b/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionTelemetry.cs index 9c04efe..719be52 100644 --- a/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionTelemetry.cs +++ b/src/Atc.Cosmos.EventStore/Diagnostics/SubscriptionTelemetry.cs @@ -1,54 +1,52 @@ -using System; using System.Diagnostics; -namespace Atc.Cosmos.EventStore.Diagnostics +namespace Atc.Cosmos.EventStore.Diagnostics; + +/// +/// Diagnostic instrumentation for event subscription. +/// +internal class SubscriptionTelemetry : ISubscriptionTelemetry { - /// - /// Diagnostic instrumentation for event subscription. - /// - internal class SubscriptionTelemetry : ISubscriptionTelemetry - { - public const string DiagnosticListenerName = "Atc.Cosmos.EventStore.Subscription.Source"; - public const string ActivityName = "Atc.Cosmos.EventStore.Subscription"; - public const string ActivityStartName = ActivityName + ".Start"; - public const string ActivityExceptionName = ActivityName + ".Exception"; + public const string DiagnosticListenerName = "Atc.Cosmos.EventStore.Subscription.Source"; + public const string ActivityName = "Atc.Cosmos.EventStore.Subscription"; + public const string ActivityStartName = ActivityName + ".Start"; + public const string ActivityExceptionName = ActivityName + ".Exception"; - private static readonly DiagnosticSource SubscriptionSource - = new DiagnosticListener(DiagnosticListenerName); + private static readonly DiagnosticSource SubscriptionSource + = new DiagnosticListener(DiagnosticListenerName); - public void ProcessExceptionHandlerFailed(Exception exception, ConsumerGroup consumerGroup) + public void ProcessExceptionHandlerFailed(Exception exception, ConsumerGroup consumerGroup) + { + if (SubscriptionSource.IsEnabled(ActivityExceptionName)) { - if (SubscriptionSource.IsEnabled(ActivityExceptionName)) - { - SubscriptionSource - .Write( - ActivityExceptionName, - new - { - Exception = exception, - ConsumerGroup = consumerGroup, - }); - } + SubscriptionSource + .Write( + ActivityExceptionName, + new + { + Exception = exception, + ConsumerGroup = consumerGroup, + }); } + } - public ISubscriptionActivity? SubscriptionStarted(ConsumerGroup consumerGroup) + public ISubscriptionActivity? SubscriptionStarted(ConsumerGroup consumerGroup) + { + if (!SubscriptionSource.IsEnabled(ActivityStartName)) { - if (!SubscriptionSource.IsEnabled(ActivityStartName)) - { - return null; - } - - var activity = new Activity(ActivityStartName); + return null; + } - SubscriptionSource - .StartActivity( - activity, - new { ConsumerGroup = consumerGroup }); + var activity = new Activity(ActivityStartName); - return new SubscriptionActivity( - SubscriptionSource, + SubscriptionSource + .StartActivity( activity, - consumerGroup); - } + new { ConsumerGroup = consumerGroup }); + + return new SubscriptionActivity( + SubscriptionSource, + activity, + consumerGroup); } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventName.cs b/src/Atc.Cosmos.EventStore/EventName.cs index 7973a4b..7b02905 100644 --- a/src/Atc.Cosmos.EventStore/EventName.cs +++ b/src/Atc.Cosmos.EventStore/EventName.cs @@ -1,47 +1,44 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public struct EventName : IEquatable { - public struct EventName : IEquatable + public EventName(string eventName) { - public EventName(string eventName) - { - Value = eventName; - } + Value = eventName; + } - public string Value { get; } + public string Value { get; } - public static implicit operator EventName(string eventName) - => new(eventName); + public static implicit operator EventName(string eventName) + => new(eventName); - public static explicit operator string(EventName eventName) - => eventName.Value; + public static explicit operator string(EventName eventName) + => eventName.Value; - public static bool operator ==(EventName left, EventName right) - => string.Equals(left.Value, right.Value, StringComparison.Ordinal); + public static bool operator ==(EventName left, EventName right) + => string.Equals(left.Value, right.Value, StringComparison.Ordinal); - public static bool operator !=(EventName left, EventName right) - => !string.Equals(left.Value, right.Value, StringComparison.Ordinal); + public static bool operator !=(EventName left, EventName right) + => !string.Equals(left.Value, right.Value, StringComparison.Ordinal); - public static EventName ToStreamId(string eventName) - => new(eventName); + public static EventName ToStreamId(string eventName) + => new(eventName); - public static string FromEventName(EventName eventName) - => eventName.Value; + public static string FromEventName(EventName eventName) + => eventName.Value; - public static EventName ToEventName(string eventName) - => new(eventName); + public static EventName ToEventName(string eventName) + => new(eventName); - public static bool Equals(EventName left, EventName right) - => left.Equals(right); + public static bool Equals(EventName left, EventName right) + => left.Equals(right); - public override bool Equals(object? obj) - => obj is EventName id && Equals(id); + public override bool Equals(object? obj) + => obj is EventName id && Equals(id); - public bool Equals(EventName other) - => string.Equals(Value, other.Value, StringComparison.Ordinal); + public bool Equals(EventName other) + => string.Equals(Value, other.Value, StringComparison.Ordinal); - public override int GetHashCode() - => HashCode.Combine(Value); - } + public override int GetHashCode() + => HashCode.Combine(Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventNotRegisteredException.cs b/src/Atc.Cosmos.EventStore/EventNotRegisteredException.cs index 328b6fd..cb91781 100644 --- a/src/Atc.Cosmos.EventStore/EventNotRegisteredException.cs +++ b/src/Atc.Cosmos.EventStore/EventNotRegisteredException.cs @@ -1,20 +1,18 @@ -using System; using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] +[SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] +public class EventNotRegisteredException : EventStoreException { - [SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] - [SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] - public class EventNotRegisteredException : EventStoreException + public EventNotRegisteredException(string message) + : base(message) { - public EventNotRegisteredException(string message) - : base(message) - { - } + } - public EventNotRegisteredException(string message, Exception innerException) - : base(message, innerException) - { - } + public EventNotRegisteredException(string message, Exception innerException) + : base(message, innerException) + { } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventStoreClient.cs b/src/Atc.Cosmos.EventStore/EventStoreClient.cs index 7c03504..fda51cd 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreClient.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreClient.cs @@ -1,139 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +internal class EventStoreClient : IEventStoreClient { - internal class EventStoreClient : IEventStoreClient - { - private readonly IStreamWriter streamWriter; - private readonly IStreamReader streamReader; - private readonly IStreamInfoReader infoReader; - private readonly IStreamIndexReader indexReader; - private readonly IStreamCheckpointWriter checkpointWriter; - private readonly IStreamCheckpointReader checkpointReader; - private readonly IStreamSubscriptionFactory subscriptionFactory; - private readonly IStreamSubscriptionRemover subscriptionRemover; + private readonly IStreamWriter streamWriter; + private readonly IStreamReader streamReader; + private readonly IStreamInfoReader infoReader; + private readonly IStreamIndexReader indexReader; + private readonly IStreamCheckpointWriter checkpointWriter; + private readonly IStreamCheckpointReader checkpointReader; + private readonly IStreamSubscriptionFactory subscriptionFactory; + private readonly IStreamSubscriptionRemover subscriptionRemover; - public EventStoreClient( - IStreamWriter streamWriter, - IStreamReader streamReader, - IStreamInfoReader infoReader, - IStreamIndexReader indexReader, - IStreamCheckpointWriter checkpointWriter, - IStreamCheckpointReader checkpointReader, - IStreamSubscriptionFactory subscriptionFactory, - IStreamSubscriptionRemover subscriptionRemover) - { - this.streamWriter = streamWriter; - this.streamReader = streamReader; - this.infoReader = infoReader; - this.indexReader = indexReader; - this.checkpointWriter = checkpointWriter; - this.checkpointReader = checkpointReader; - this.subscriptionFactory = subscriptionFactory; - this.subscriptionRemover = subscriptionRemover; - } + public EventStoreClient( + IStreamWriter streamWriter, + IStreamReader streamReader, + IStreamInfoReader infoReader, + IStreamIndexReader indexReader, + IStreamCheckpointWriter checkpointWriter, + IStreamCheckpointReader checkpointReader, + IStreamSubscriptionFactory subscriptionFactory, + IStreamSubscriptionRemover subscriptionRemover) + { + this.streamWriter = streamWriter; + this.streamReader = streamReader; + this.infoReader = infoReader; + this.indexReader = indexReader; + this.checkpointWriter = checkpointWriter; + this.checkpointReader = checkpointReader; + this.subscriptionFactory = subscriptionFactory; + this.subscriptionRemover = subscriptionRemover; + } - public Task DeleteSubscriptionAsync( - ConsumerGroup consumerGroup, - CancellationToken cancellationToken = default) - => subscriptionRemover - .DeleteAsync( - Arguments.EnsureNotNull(consumerGroup, nameof(consumerGroup)), - cancellationToken); + public Task DeleteSubscriptionAsync( + ConsumerGroup consumerGroup, + CancellationToken cancellationToken = default) + => subscriptionRemover + .DeleteAsync( + Arguments.EnsureNotNull(consumerGroup, nameof(consumerGroup)), + cancellationToken); - public Task GetStreamInfoAsync( - StreamId streamId, - CancellationToken cancellationToken = default) - => infoReader - .ReadAsync( - streamId, - cancellationToken); + public Task GetStreamInfoAsync( + StreamId streamId, + CancellationToken cancellationToken = default) + => infoReader + .ReadAsync( + streamId, + cancellationToken); - public IAsyncEnumerable QueryStreamsAsync( - string? filter = default, - DateTimeOffset? createdAfter = default, - CancellationToken cancellationToken = default) - => indexReader - .ReadAsync( - filter, - createdAfter, - cancellationToken); + public IAsyncEnumerable QueryStreamsAsync( + string? filter = default, + DateTimeOffset? createdAfter = default, + CancellationToken cancellationToken = default) + => indexReader + .ReadAsync( + filter, + createdAfter, + cancellationToken); - public IAsyncEnumerable ReadFromStreamAsync( - StreamId streamId, - StreamVersion? fromVersion = default, - StreamReadFilter? filter = default, - CancellationToken cancellationToken = default) - => streamReader - .ReadAsync( - streamId, - Arguments.EnsureValueRange(fromVersion ?? StreamVersion.Any, nameof(fromVersion)), - filter, - cancellationToken); + public IAsyncEnumerable ReadFromStreamAsync( + StreamId streamId, + StreamVersion? fromVersion = default, + StreamReadFilter? filter = default, + CancellationToken cancellationToken = default) + => streamReader + .ReadAsync( + streamId, + Arguments.EnsureValueRange(fromVersion ?? StreamVersion.Any, nameof(fromVersion)), + filter, + cancellationToken); - public IStreamSubscription SubscribeToStreams( - ConsumerGroup consumerGroup, - SubscriptionStartOptions startOptions, - ProcessEventsHandler eventsHandler, - ProcessExceptionHandler exceptionHandler) - => subscriptionFactory - .Create( - Arguments.EnsureNotNull(consumerGroup, nameof(consumerGroup)), - startOptions, - Arguments.EnsureNotNull(eventsHandler, nameof(eventsHandler)), - Arguments.EnsureNotNull(exceptionHandler, nameof(exceptionHandler))); + public IStreamSubscription SubscribeToStreams( + ConsumerGroup consumerGroup, + SubscriptionStartOptions startOptions, + ProcessEventsHandler eventsHandler, + ProcessExceptionHandler exceptionHandler) + => subscriptionFactory + .Create( + Arguments.EnsureNotNull(consumerGroup, nameof(consumerGroup)), + startOptions, + Arguments.EnsureNotNull(eventsHandler, nameof(eventsHandler)), + Arguments.EnsureNotNull(exceptionHandler, nameof(exceptionHandler))); - public Task WriteToStreamAsync( - StreamId streamId, - IReadOnlyCollection events, - StreamVersion? version = default, - StreamWriteOptions? options = default, - CancellationToken cancellationToken = default) - => streamWriter - .WriteAsync( - streamId, - Arguments.EnsureNoNullValues(events, nameof(events)), - version ?? StreamVersion.Any, - options, - cancellationToken); + public Task WriteToStreamAsync( + StreamId streamId, + IReadOnlyCollection events, + StreamVersion? version = default, + StreamWriteOptions? options = default, + CancellationToken cancellationToken = default) + => streamWriter + .WriteAsync( + streamId, + Arguments.EnsureNoNullValues(events, nameof(events)), + version ?? StreamVersion.Any, + options, + cancellationToken); - public Task SetStreamCheckpointAsync( - string name, - StreamId streamId, - StreamVersion version, - object? state = null, - CancellationToken cancellationToken = default) - => checkpointWriter - .WriteAsync( - Arguments.EnsureNotNull(name, nameof(name)), - streamId, - version, - state, - cancellationToken); + public Task SetStreamCheckpointAsync( + string name, + StreamId streamId, + StreamVersion version, + object? state = null, + CancellationToken cancellationToken = default) + => checkpointWriter + .WriteAsync( + Arguments.EnsureNotNull(name, nameof(name)), + streamId, + version, + state, + cancellationToken); - public Task?> GetStreamCheckpointAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken = default) - => checkpointReader - .ReadAsync( - Arguments.EnsureNotNull(name, nameof(name)), - streamId, - cancellationToken); + public Task?> GetStreamCheckpointAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken = default) + => checkpointReader + .ReadAsync( + Arguments.EnsureNotNull(name, nameof(name)), + streamId, + cancellationToken); - public async Task GetStreamCheckpointAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken = default) - => await checkpointReader - .ReadAsync( - Arguments.EnsureNotNull(name, nameof(name)), - streamId, - cancellationToken) - .ConfigureAwait(false); - } + public async Task GetStreamCheckpointAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken = default) + => await checkpointReader + .ReadAsync( + Arguments.EnsureNotNull(name, nameof(name)), + streamId, + cancellationToken) + .ConfigureAwait(false); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs index 51bed92..5706c7d 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs @@ -1,114 +1,111 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; using Atc.Cosmos.EventStore.Converters; using Azure.Core; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public class EventStoreClientOptions { - public class EventStoreClientOptions - { - public const string EmulatorEndpoint = "https://localhost:8081/"; - public const string EmulatorAuthKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + public const string EmulatorEndpoint = "https://localhost:8081/"; + public const string EmulatorAuthKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - [Obsolete("Call UseCosmosEmulator instead.")] - public const string CosmosEmulatorConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; + [Obsolete("Call UseCosmosEmulator instead.")] + public const string CosmosEmulatorConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; #pragma warning disable CS0618 // Type or member is obsolete - private string? connectionString = CosmosEmulatorConnectionString; + private string? connectionString = CosmosEmulatorConnectionString; #pragma warning restore CS0618 // Type or member is obsolete - [Obsolete("Call UseCredentials instead.")] - public string? ConnectionString + [Obsolete("Call UseCredentials instead.")] + public string? ConnectionString + { + get + { + return connectionString; + } + + set { - get - { - return connectionString; - } - - set - { - connectionString = value; - AuthKey = null; - Endpoint = null; - Credential = null; - } + connectionString = value; + AuthKey = null; + Endpoint = null; + Credential = null; } + } - public string EventStoreDatabaseId { get; set; } = "EventStore"; + public string EventStoreDatabaseId { get; set; } = "EventStore"; - public string EventStoreContainerId { get; set; } = "event-store"; + public string EventStoreContainerId { get; set; } = "event-store"; - public string IndexContainerId { get; set; } = "stream-index"; + public string IndexContainerId { get; set; } = "stream-index"; - public string SubscriptionContainerId { get; set; } = "subscriptions"; + public string SubscriptionContainerId { get; set; } = "subscriptions"; - public CosmosClientOptions CosmosClientOptions { get; set; } = new CosmosClientOptions(); + public CosmosClientOptions CosmosClientOptions { get; set; } = new CosmosClientOptions(); - /// - /// Gets collections of custom . - /// - public ICollection CustomJsonConverter { get; } = new List(); + /// + /// Gets collections of custom . + /// + public ICollection CustomJsonConverter { get; } = new List(); - /// - /// Gets collections of custom event data converters. - /// - public ICollection EventDataConverter { get; } = new List(); + /// + /// Gets collections of custom event data converters. + /// + public ICollection EventDataConverter { get; } = new List(); - public string? Endpoint { get; private set; } + public string? Endpoint { get; private set; } - public string? AuthKey { get; private set; } + public string? AuthKey { get; private set; } - public TokenCredential? Credential { get; private set; } + public TokenCredential? Credential { get; private set; } - /// - /// Configure event store to use instead . - /// - /// Cosmos account endpoint. - /// Token credentials to use when connecting to cosmos. - /// Throws when or are null. - public void UseCredentials( - string endpoint, - TokenCredential credentials) - { - Credential = credentials ?? throw new ArgumentNullException(nameof(credentials)); - Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); - AuthKey = null; + /// + /// Configure event store to use instead . + /// + /// Cosmos account endpoint. + /// Token credentials to use when connecting to cosmos. + /// Throws when or are null. + public void UseCredentials( + string endpoint, + TokenCredential credentials) + { + Credential = credentials ?? throw new ArgumentNullException(nameof(credentials)); + Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); + AuthKey = null; #pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; + connectionString = null; #pragma warning restore CS0618 // Type or member is obsolete - } + } - /// - /// Configure event storte to use AuthKey when connecting to cosmos db. - /// - /// Cosmos account endpoint. - /// Authorization key to connect with. - /// Throws when or are null. - public void UseCredentials( - string endpoint, - string authKey) - { - Credential = null; - Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); - AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey)); + /// + /// Configure event storte to use AuthKey when connecting to cosmos db. + /// + /// Cosmos account endpoint. + /// Authorization key to connect with. + /// Throws when or are null. + public void UseCredentials( + string endpoint, + string authKey) + { + Credential = null; + Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); + AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey)); #pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; + connectionString = null; #pragma warning restore CS0618 // Type or member is obsolete - } + } - /// - /// Configure event store to use cosmos emulator. - /// - public void UseCosmosEmulator() - { - Credential = null; - Endpoint = EmulatorEndpoint; - AuthKey = EmulatorAuthKey; + /// + /// Configure event store to use cosmos emulator. + /// + public void UseCosmosEmulator() + { + Credential = null; + Endpoint = EmulatorEndpoint; + AuthKey = EmulatorAuthKey; #pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; + connectionString = null; #pragma warning restore CS0618 // Type or member is obsolete - } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventStoreException.cs b/src/Atc.Cosmos.EventStore/EventStoreException.cs index 4cab149..f120ec7 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreException.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreException.cs @@ -1,23 +1,21 @@ -using System; using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +[SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] +public abstract class EventStoreException : Exception { - [SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] - public abstract class EventStoreException : Exception + protected EventStoreException() { - protected EventStoreException() - { - } + } - protected EventStoreException(string message) - : base(message) - { - } + protected EventStoreException(string message) + : base(message) + { + } - protected EventStoreException(string message, Exception innerException) - : base(message, innerException) - { - } + protected EventStoreException(string message, Exception innerException) + : base(message, innerException) + { } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/EventStoreManagementClient.cs b/src/Atc.Cosmos.EventStore/EventStoreManagementClient.cs index ee1143c..7c59610 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreManagementClient.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreManagementClient.cs @@ -1,27 +1,22 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +internal class EventStoreManagementClient : IEventStoreManagementClient { - internal class EventStoreManagementClient : IEventStoreManagementClient - { - public Task DeleteStreamAsync( - StreamId streamId, - CancellationToken cancellationToken = default) - => throw new NotImplementedException(); + public Task DeleteStreamAsync( + StreamId streamId, + CancellationToken cancellationToken = default) + => throw new NotImplementedException(); - public Task PurgeStreamAsync( - StreamId streamId, - StreamVersion version, - long count, - CancellationToken cancellationToken = default) - => throw new NotImplementedException(); + public Task PurgeStreamAsync( + StreamId streamId, + StreamVersion version, + long count, + CancellationToken cancellationToken = default) + => throw new NotImplementedException(); - public Task RetireStreamAsync( - StreamId streamId, - StreamVersion? expectedVersion = default, - CancellationToken cancellationToken = default) - => throw new System.NotImplementedException(); - } + public Task RetireStreamAsync( + StreamId streamId, + StreamVersion? expectedVersion = default, + CancellationToken cancellationToken = default) + => throw new System.NotImplementedException(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs index 5423106..16f7aa8 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Atc.Cosmos.EventStore.Streams; namespace Atc.Cosmos.EventStore.Events; diff --git a/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs b/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs index 0220b10..72e6ebd 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventCatalog.cs @@ -1,41 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.Events +/// +/// The EventCatalog is responsible for mapping an event object to a name +/// and from an event name to an object type. +/// +/// The name is whats stored in the persisted event. +internal class EventCatalog : IEventCatalog { - /// - /// The EventCatalog is responsible for mapping an event object to a name - /// and from an event name to an object type. - /// - /// The name is whats stored in the persisted event. - internal class EventCatalog : IEventCatalog - { - private readonly IReadOnlyDictionary mappings; + private readonly IReadOnlyDictionary mappings; - public EventCatalog(IReadOnlyDictionary mappings) - => this.mappings = mappings; + public EventCatalog(IReadOnlyDictionary mappings) + => this.mappings = mappings; - public Type? GetEventType(EventName name) - => mappings.TryGetValue(name, out var type) - ? type - : default; + public Type? GetEventType(EventName name) + => mappings.TryGetValue(name, out var type) + ? type + : default; - public string GetName(object evt) + public string GetName(object evt) + { + try + { + return mappings + .First(kvp => kvp.Value == evt.GetType()) + .Key + .Value; + } + catch (InvalidOperationException ex) { - try - { - return mappings - .First(kvp => kvp.Value == evt.GetType()) - .Key - .Value; - } - catch (InvalidOperationException ex) - { - throw new EventNotRegisteredException( - evt.GetType().Name, - ex); - } + throw new EventNotRegisteredException( + evt.GetType().Name, + ex); } } } diff --git a/src/Atc.Cosmos.EventStore/Events/EventDocument{T}.cs b/src/Atc.Cosmos.EventStore/Events/EventDocument{T}.cs index 89129df..8b019ce 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventDocument{T}.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventDocument{T}.cs @@ -1,29 +1,28 @@ using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +internal class EventDocument : EventDocument + where T : class { - internal class EventDocument : EventDocument - where T : class + public EventDocument() { - public EventDocument() - { - } + } - public EventDocument(T data, EventMetadata properties) - : base() - { - TypedData = data; - Properties = properties; - } + public EventDocument(T data, EventMetadata properties) + : base() + { + TypedData = data; + Properties = properties; + } - [JsonPropertyName(EventMetadataNames.Data)] - public T TypedData { get; set; } = default!; + [JsonPropertyName(EventMetadataNames.Data)] + public T TypedData { get; set; } = default!; - [JsonIgnore] - public override object Data - { - get { return TypedData; } - set { TypedData = (T)value; } - } + [JsonIgnore] + public override object Data + { + get { return TypedData; } + set { TypedData = (T)value; } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs b/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs index 59acc0e..e2afdc0 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs @@ -1,27 +1,25 @@ -using System; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +/// +/// Represents a set of properties related to an events. +/// +internal class EventMetadata : IEventMetadata { - /// - /// Represents a set of properties related to an events. - /// - internal class EventMetadata : IEventMetadata - { - [JsonIgnore] - public string EventId { get; set; } = string.Empty; + [JsonIgnore] + public string EventId { get; set; } = string.Empty; - [JsonPropertyName(EventMetadataNames.EventName)] - public string Name { get; set; } = string.Empty; + [JsonPropertyName(EventMetadataNames.EventName)] + public string Name { get; set; } = string.Empty; - public StreamId StreamId { get; set; } = string.Empty; + public StreamId StreamId { get; set; } = string.Empty; - public StreamVersion Version { get; set; } + public StreamVersion Version { get; set; } - public DateTimeOffset Timestamp { get; set; } + public DateTimeOffset Timestamp { get; set; } - public string? CorrelationId { get; set; } + public string? CorrelationId { get; set; } - public string? CausationId { get; set; } - } + public string? CausationId { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/EventMetadataNames.cs b/src/Atc.Cosmos.EventStore/Events/EventMetadataNames.cs index 9b34684..d5be3f7 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventMetadataNames.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventMetadataNames.cs @@ -1,11 +1,10 @@ -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +internal static class EventMetadataNames { - internal static class EventMetadataNames - { - public const string Id = "id"; - public const string PartitionKey = "pk"; - public const string Properties = "properties"; - public const string EventName = "name"; - public const string Data = "data"; - } + public const string Id = "id"; + public const string PartitionKey = "pk"; + public const string Properties = "properties"; + public const string EventName = "name"; + public const string Data = "data"; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/IDateTimeProvider.cs b/src/Atc.Cosmos.EventStore/Events/IDateTimeProvider.cs index c31b622..5b15123 100644 --- a/src/Atc.Cosmos.EventStore/Events/IDateTimeProvider.cs +++ b/src/Atc.Cosmos.EventStore/Events/IDateTimeProvider.cs @@ -1,9 +1,6 @@ -using System; +namespace Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.Events +public interface IDateTimeProvider { - public interface IDateTimeProvider - { - DateTimeOffset GetDateTime(); - } + DateTimeOffset GetDateTime(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/IEventCatalog.cs b/src/Atc.Cosmos.EventStore/Events/IEventCatalog.cs index 83d290d..16c417b 100644 --- a/src/Atc.Cosmos.EventStore/Events/IEventCatalog.cs +++ b/src/Atc.Cosmos.EventStore/Events/IEventCatalog.cs @@ -1,6 +1,5 @@ -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +public interface IEventCatalog : IEventNameProvider, IEventTypeProvider { - public interface IEventCatalog : IEventNameProvider, IEventTypeProvider - { - } } diff --git a/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs b/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs index 5d1a54e..4f5b5a0 100644 --- a/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs +++ b/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +public interface IEventIdProvider { - public interface IEventIdProvider - { - string CreateUniqueId(IStreamMetadata metadata); - } + string CreateUniqueId(IStreamMetadata metadata); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/IEventNameProvider.cs b/src/Atc.Cosmos.EventStore/Events/IEventNameProvider.cs index ebad597..aee8509 100644 --- a/src/Atc.Cosmos.EventStore/Events/IEventNameProvider.cs +++ b/src/Atc.Cosmos.EventStore/Events/IEventNameProvider.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Events +namespace Atc.Cosmos.EventStore.Events; + +public interface IEventNameProvider { - public interface IEventNameProvider - { - string GetName(object evt); - } + string GetName(object evt); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs b/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs index b4673c8..4aa2468 100644 --- a/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs +++ b/src/Atc.Cosmos.EventStore/Events/IEventTypeProvider.cs @@ -1,5 +1,3 @@ -using System; - namespace Atc.Cosmos.EventStore.Events; public interface IEventTypeProvider diff --git a/src/Atc.Cosmos.EventStore/FaultedEvent.cs b/src/Atc.Cosmos.EventStore/FaultedEvent.cs index b1703a5..e0e10aa 100644 --- a/src/Atc.Cosmos.EventStore/FaultedEvent.cs +++ b/src/Atc.Cosmos.EventStore/FaultedEvent.cs @@ -1,5 +1,3 @@ -using System; - namespace Atc.Cosmos.EventStore; /// diff --git a/src/Atc.Cosmos.EventStore/IEvent.cs b/src/Atc.Cosmos.EventStore/IEvent.cs index 8f96c66..38a82e9 100644 --- a/src/Atc.Cosmos.EventStore/IEvent.cs +++ b/src/Atc.Cosmos.EventStore/IEvent.cs @@ -1,15 +1,14 @@ -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public interface IEvent { - public interface IEvent - { - /// - /// Gets event data. - /// - object Data { get; } + /// + /// Gets event data. + /// + object Data { get; } - /// - /// Gets event metadata. - /// - IEventMetadata Metadata { get; } - } + /// + /// Gets event metadata. + /// + IEventMetadata Metadata { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IEventMetadata.cs b/src/Atc.Cosmos.EventStore/IEventMetadata.cs index 3ecd9e0..cb7c184 100644 --- a/src/Atc.Cosmos.EventStore/IEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore/IEventMetadata.cs @@ -1,46 +1,43 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +/// +/// Represents meta-data of an event in a stream. +/// +public interface IEventMetadata { /// - /// Represents meta-data of an event in a stream. + /// Gets the unique id of the event. /// - public interface IEventMetadata - { - /// - /// Gets the unique id of the event. - /// - string EventId { get; } + string EventId { get; } - /// - /// Gets the name of the event. - /// - string Name { get; } + /// + /// Gets the name of the event. + /// + string Name { get; } - /// - /// Gets the correlation id associated with the event. - /// - string? CorrelationId { get; } + /// + /// Gets the correlation id associated with the event. + /// + string? CorrelationId { get; } - /// - /// Gets the causation id associated with the event. - /// - string? CausationId { get; } + /// + /// Gets the causation id associated with the event. + /// + string? CausationId { get; } - /// - /// Gets the id of the stream. - /// - StreamId StreamId { get; } + /// + /// Gets the id of the stream. + /// + StreamId StreamId { get; } - /// - /// Gets when the event was created. - /// - DateTimeOffset Timestamp { get; } + /// + /// Gets when the event was created. + /// + DateTimeOffset Timestamp { get; } - /// - /// Gets the event version. - /// What version this event is within a stream. - /// - StreamVersion Version { get; } - } + /// + /// Gets the event version. + /// What version this event is within a stream. + /// + StreamVersion Version { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IEventStoreClient.cs b/src/Atc.Cosmos.EventStore/IEventStoreClient.cs index 863f639..6461f70 100644 --- a/src/Atc.Cosmos.EventStore/IEventStoreClient.cs +++ b/src/Atc.Cosmos.EventStore/IEventStoreClient.cs @@ -1,140 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public interface IEventStoreClient { - public interface IEventStoreClient - { - /// - /// Writes a collection of event objects to a stream. - /// - /// Event stream to write events too. - /// Collection of event objects to write. - /// - /// (Optional) Set the required version of the stream. - /// Set this value to the latest known version of the stream to enable optimistic currency. - /// When the stream is required to be empty use . - /// Use when the stream must contain one or more events. - /// To append to the end of the stream optionally specify . - /// - /// (Optional) The options for writing events. - /// (Optional) representing request cancellation. - /// Response of the write operation. - Task WriteToStreamAsync( - StreamId streamId, - IReadOnlyCollection events, - StreamVersion? version = default, - StreamWriteOptions? options = default, - CancellationToken cancellationToken = default); + /// + /// Writes a collection of event objects to a stream. + /// + /// Event stream to write events too. + /// Collection of event objects to write. + /// + /// (Optional) Set the required version of the stream. + /// Set this value to the latest known version of the stream to enable optimistic currency. + /// When the stream is required to be empty use . + /// Use when the stream must contain one or more events. + /// To append to the end of the stream optionally specify . + /// + /// (Optional) The options for writing events. + /// (Optional) representing request cancellation. + /// Response of the write operation. + Task WriteToStreamAsync( + StreamId streamId, + IReadOnlyCollection events, + StreamVersion? version = default, + StreamWriteOptions? options = default, + CancellationToken cancellationToken = default); - /// - /// Read events from stream. - /// - /// Event stream to read from. - /// (Optional) Start reading stream from a given version. - /// (Optional) Specify a filter to only include certain events, and/or ensure stream is at a given version. - /// (Optional) representing request cancellation. - /// List of events from stream. - IAsyncEnumerable ReadFromStreamAsync( - StreamId streamId, - StreamVersion? fromVersion = default, - StreamReadFilter? filter = default, - CancellationToken cancellationToken = default); + /// + /// Read events from stream. + /// + /// Event stream to read from. + /// (Optional) Start reading stream from a given version. + /// (Optional) Specify a filter to only include certain events, and/or ensure stream is at a given version. + /// (Optional) representing request cancellation. + /// List of events from stream. + IAsyncEnumerable ReadFromStreamAsync( + StreamId streamId, + StreamVersion? fromVersion = default, + StreamReadFilter? filter = default, + CancellationToken cancellationToken = default); - /// - /// Gets current state for a specific stream. - /// - /// Event stream to read from. - /// (Optional) representing request cancellation. - /// Stream information. - Task GetStreamInfoAsync( - StreamId streamId, - CancellationToken cancellationToken = default); + /// + /// Gets current state for a specific stream. + /// + /// Event stream to read from. + /// (Optional) representing request cancellation. + /// Stream information. + Task GetStreamInfoAsync( + StreamId streamId, + CancellationToken cancellationToken = default); - /// - /// Gets or creates an event subscription for all streams. - /// - /// The name the subscription is persisted with. - /// Start options for subscription. - /// Delegate called when events arrives. - /// Delegate called when an exception occurred. - /// Event subscription. - IStreamSubscription SubscribeToStreams( - ConsumerGroup consumerGroup, - SubscriptionStartOptions startOptions, - ProcessEventsHandler eventsHandler, - ProcessExceptionHandler exceptionHandler); + /// + /// Gets or creates an event subscription for all streams. + /// + /// The name the subscription is persisted with. + /// Start options for subscription. + /// Delegate called when events arrives. + /// Delegate called when an exception occurred. + /// Event subscription. + IStreamSubscription SubscribeToStreams( + ConsumerGroup consumerGroup, + SubscriptionStartOptions startOptions, + ProcessEventsHandler eventsHandler, + ProcessExceptionHandler exceptionHandler); - /// - /// Delete subscription. - /// - /// Consumer group to remove subscription from. - /// (Optional) representing request cancellation. - /// A representing the asynchronous operation. - Task DeleteSubscriptionAsync( - ConsumerGroup consumerGroup, - CancellationToken cancellationToken = default); + /// + /// Delete subscription. + /// + /// Consumer group to remove subscription from. + /// (Optional) representing request cancellation. + /// A representing the asynchronous operation. + Task DeleteSubscriptionAsync( + ConsumerGroup consumerGroup, + CancellationToken cancellationToken = default); - /// - /// Search for streams matching a given filter expression. - /// - /// - /// Filter expression for finding desired streams. - /// - /// - /// (Optional) exclude streams created prior to this timestamp. - /// (Optional) representing request cancellation. - /// List of stream id found. - IAsyncEnumerable QueryStreamsAsync( - string? filter = default, - DateTimeOffset? createdAfter = default, - CancellationToken cancellationToken = default); + /// + /// Search for streams matching a given filter expression. + /// + /// + /// Filter expression for finding desired streams. + /// + /// + /// (Optional) exclude streams created prior to this timestamp. + /// (Optional) representing request cancellation. + /// List of stream id found. + IAsyncEnumerable QueryStreamsAsync( + string? filter = default, + DateTimeOffset? createdAfter = default, + CancellationToken cancellationToken = default); - /// - /// Sets a named checkpoint at a given version in the stream. - /// - /// - /// Only one checkpoint per name can exists at any given time. - /// A checkpoint will be overridden when using an existing name. - /// - /// Name of checkpoint. - /// Id of stream. - /// Version within the stream this checkpoint is related too. - /// (Optional) State object to store along side the checkpoint. - /// (Optional) representing request cancellation. - /// A representing the asynchronous operation. - Task SetStreamCheckpointAsync( - string name, - StreamId streamId, - StreamVersion version, - object? state = default, - CancellationToken cancellationToken = default); + /// + /// Sets a named checkpoint at a given version in the stream. + /// + /// + /// Only one checkpoint per name can exists at any given time. + /// A checkpoint will be overridden when using an existing name. + /// + /// Name of checkpoint. + /// Id of stream. + /// Version within the stream this checkpoint is related too. + /// (Optional) State object to store along side the checkpoint. + /// (Optional) representing request cancellation. + /// A representing the asynchronous operation. + Task SetStreamCheckpointAsync( + string name, + StreamId streamId, + StreamVersion version, + object? state = default, + CancellationToken cancellationToken = default); - /// - /// Gets a named checkpoint with state from a stream. - /// - /// Type of state. - /// Name of checkpoint. - /// Id of stream. - /// (Optional) representing request cancellation. - /// A statefull or null if not found. - Task?> GetStreamCheckpointAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken = default); + /// + /// Gets a named checkpoint with state from a stream. + /// + /// Type of state. + /// Name of checkpoint. + /// Id of stream. + /// (Optional) representing request cancellation. + /// A statefull or null if not found. + Task?> GetStreamCheckpointAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken = default); - /// - /// Gets a named checkpoint from a stream. - /// - /// Name of checkpoint. - /// Id of stream. - /// (Optional) representing request cancellation. - /// A or null if not found. - Task GetStreamCheckpointAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken = default); - } + /// + /// Gets a named checkpoint from a stream. + /// + /// Name of checkpoint. + /// Id of stream. + /// (Optional) representing request cancellation. + /// A or null if not found. + Task GetStreamCheckpointAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IEventStoreManagementClient.cs b/src/Atc.Cosmos.EventStore/IEventStoreManagementClient.cs index ea46220..d615184 100644 --- a/src/Atc.Cosmos.EventStore/IEventStoreManagementClient.cs +++ b/src/Atc.Cosmos.EventStore/IEventStoreManagementClient.cs @@ -1,55 +1,51 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +internal interface IEventStoreManagementClient { - internal interface IEventStoreManagementClient - { - /// - /// Mark stream as closed, preventing any further writes. - /// - /// - /// Set to the current stream version to ensure no new events has been written to the - /// stream prior to executing the delete operation. - /// - /// Id of the event stream to delete. - /// (Optional) Specify the expected version the stream to be at to allow deletion. - /// (Optional) representing request cancellation. - /// Response of the end operation. - Task RetireStreamAsync( - StreamId streamId, - StreamVersion? expectedVersion = default, - CancellationToken cancellationToken = default); + /// + /// Mark stream as closed, preventing any further writes. + /// + /// + /// Set to the current stream version to ensure no new events has been written to the + /// stream prior to executing the delete operation. + /// + /// Id of the event stream to delete. + /// (Optional) Specify the expected version the stream to be at to allow deletion. + /// (Optional) representing request cancellation. + /// Response of the end operation. + Task RetireStreamAsync( + StreamId streamId, + StreamVersion? expectedVersion = default, + CancellationToken cancellationToken = default); - /// - /// Purge events from a stream. - /// - /// - /// Using a negative value for will purge backwards from .
- /// To purge the entire stream set to and to 0.
- /// - /// This operation can not be revoked as purged events within the stream will be deleted. - ///
- /// Id of the event stream to purge. - /// Specifies the version of the first event to purge from stream. - /// Number of events to purge. - /// (Optional) representing request cancellation. - /// A representing the asynchronous operation. - Task PurgeStreamAsync( - StreamId streamId, - StreamVersion version, - long count, - CancellationToken cancellationToken = default); + /// + /// Purge events from a stream. + /// + /// + /// Using a negative value for will purge backwards from .
+ /// To purge the entire stream set to and to 0.
+ /// + /// This operation can not be revoked as purged events within the stream will be deleted. + ///
+ /// Id of the event stream to purge. + /// Specifies the version of the first event to purge from stream. + /// Number of events to purge. + /// (Optional) representing request cancellation. + /// A representing the asynchronous operation. + Task PurgeStreamAsync( + StreamId streamId, + StreamVersion version, + long count, + CancellationToken cancellationToken = default); - /// - /// Deletes an entire stream and it's index. - /// - /// Attempting to write to a deleted stream will create a new empty stream. - /// Id of the event stream to delete. - /// (Optional) representing request cancellation. - /// A representing the asynchronous operation. - Task DeleteStreamAsync( - StreamId streamId, - CancellationToken cancellationToken = default); - } + /// + /// Deletes an entire stream and it's index. + /// + /// Attempting to write to a deleted stream will create a new empty stream. + /// Id of the event stream to delete. + /// (Optional) representing request cancellation. + /// A representing the asynchronous operation. + Task DeleteStreamAsync( + StreamId streamId, + CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IStreamIndex.cs b/src/Atc.Cosmos.EventStore/IStreamIndex.cs index 1d600b1..de6a40c 100644 --- a/src/Atc.Cosmos.EventStore/IStreamIndex.cs +++ b/src/Atc.Cosmos.EventStore/IStreamIndex.cs @@ -1,16 +1,13 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public interface IStreamIndex { - public interface IStreamIndex - { - StreamId StreamId { get; } + StreamId StreamId { get; } - /// - /// Gets Timestamp for when the stream was created. - /// - DateTimeOffset Timestamp { get; } + /// + /// Gets Timestamp for when the stream was created. + /// + DateTimeOffset Timestamp { get; } - bool IsActive { get; } - } + bool IsActive { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IStreamMetadata.cs b/src/Atc.Cosmos.EventStore/IStreamMetadata.cs index c9a74aa..5cbd485 100644 --- a/src/Atc.Cosmos.EventStore/IStreamMetadata.cs +++ b/src/Atc.Cosmos.EventStore/IStreamMetadata.cs @@ -1,17 +1,14 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public interface IStreamMetadata { - public interface IStreamMetadata - { - StreamState State { get; } + StreamState State { get; } - StreamId StreamId { get; } + StreamId StreamId { get; } - DateTimeOffset Timestamp { get; } + DateTimeOffset Timestamp { get; } - StreamVersion Version { get; } + StreamVersion Version { get; } - string ETag { get; } - } + string ETag { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IStreamSubscription.cs b/src/Atc.Cosmos.EventStore/IStreamSubscription.cs index 177c05f..635cb7c 100644 --- a/src/Atc.Cosmos.EventStore/IStreamSubscription.cs +++ b/src/Atc.Cosmos.EventStore/IStreamSubscription.cs @@ -1,19 +1,16 @@ -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public interface IStreamSubscription { - public interface IStreamSubscription - { - /// - /// Start processing subscription. - /// - /// A representing the asynchronous operation. - Task StartAsync(); + /// + /// Start processing subscription. + /// + /// A representing the asynchronous operation. + Task StartAsync(); - /// - /// Stop processing. - /// - /// A representing the asynchronous operation. - Task StopAsync(); - } + /// + /// Stop processing. + /// + /// A representing the asynchronous operation. + Task StopAsync(); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/InMemory/InMemoryStore.cs b/src/Atc.Cosmos.EventStore/InMemory/InMemoryStore.cs index 503ab4a..4cb859e 100644 --- a/src/Atc.Cosmos.EventStore/InMemory/InMemoryStore.cs +++ b/src/Atc.Cosmos.EventStore/InMemory/InMemoryStore.cs @@ -1,94 +1,89 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.InMemory +namespace Atc.Cosmos.EventStore.InMemory; + +internal class InMemoryStore : + IStreamMetadataReader, + IStreamIterator, + IStreamBatchWriter, + IStreamSubscriptionFactory, + IStreamSubscriptionRemover, + IStreamIndexReader, + IStreamCheckpointReader, + IStreamCheckpointWriter { - internal class InMemoryStore : - IStreamMetadataReader, - IStreamIterator, - IStreamBatchWriter, - IStreamSubscriptionFactory, - IStreamSubscriptionRemover, - IStreamIndexReader, - IStreamCheckpointReader, - IStreamCheckpointWriter - { - private readonly IDateTimeProvider dateTimeProvider; + private readonly IDateTimeProvider dateTimeProvider; - public InMemoryStore( - IDateTimeProvider dateTimeProvider) - { - this.dateTimeProvider = dateTimeProvider; - } + public InMemoryStore( + IDateTimeProvider dateTimeProvider) + { + this.dateTimeProvider = dateTimeProvider; + } - public ConcurrentDictionary> EventStore { get; } - = new ConcurrentDictionary>(); + public ConcurrentDictionary> EventStore { get; } + = new ConcurrentDictionary>(); - public ConcurrentDictionary> Checkpoints { get; } - = new ConcurrentDictionary>(); + public ConcurrentDictionary> Checkpoints { get; } + = new ConcurrentDictionary>(); - IStreamSubscription IStreamSubscriptionFactory.Create( - ConsumerGroup consumerGroup, - SubscriptionStartOptions startOptions, - ProcessEventsHandler eventsHandler, - ProcessExceptionHandler exceptionHandler) - => throw new NotImplementedException(); + IStreamSubscription IStreamSubscriptionFactory.Create( + ConsumerGroup consumerGroup, + SubscriptionStartOptions startOptions, + ProcessEventsHandler eventsHandler, + ProcessExceptionHandler exceptionHandler) + => throw new NotImplementedException(); - Task IStreamSubscriptionRemover.DeleteAsync( - ConsumerGroup consumerGroup, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + Task IStreamSubscriptionRemover.DeleteAsync( + ConsumerGroup consumerGroup, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - Task IStreamMetadataReader.GetAsync( - StreamId streamId, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + Task IStreamMetadataReader.GetAsync( + StreamId streamId, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - IAsyncEnumerable IStreamIterator.ReadAsync( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + IAsyncEnumerable IStreamIterator.ReadAsync( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - IAsyncEnumerable IStreamIndexReader.ReadAsync( - string? filter, - DateTimeOffset? createdAfter, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + IAsyncEnumerable IStreamIndexReader.ReadAsync( + string? filter, + DateTimeOffset? createdAfter, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - Task?> IStreamCheckpointReader.ReadAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + Task?> IStreamCheckpointReader.ReadAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - Task IStreamBatchWriter.WriteAsync( - StreamBatch batch, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + Task IStreamBatchWriter.WriteAsync( + StreamBatch batch, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task WriteAsync( - string name, - StreamId streamId, - StreamVersion streamVersion, - object? state, - CancellationToken cancellationToken) - { - Checkpoints - .GetOrAdd(streamId, new ConcurrentDictionary()) - .AddOrUpdate( - name, - key => new CheckpointDocument(name, streamId, streamVersion, dateTimeProvider.GetDateTime(), state), - (key, doc) => new CheckpointDocument(name, streamId, streamVersion, dateTimeProvider.GetDateTime(), state)); + public Task WriteAsync( + string name, + StreamId streamId, + StreamVersion streamVersion, + object? state, + CancellationToken cancellationToken) + { + Checkpoints + .GetOrAdd(streamId, new ConcurrentDictionary()) + .AddOrUpdate( + name, + key => new CheckpointDocument(name, streamId, streamVersion, dateTimeProvider.GetDateTime(), state), + (key, doc) => new CheckpointDocument(name, streamId, streamVersion, dateTimeProvider.GetDateTime(), state)); - return Task.CompletedTask; - } + return Task.CompletedTask; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/InMemory/InMemoryStoreInitializer.cs b/src/Atc.Cosmos.EventStore/InMemory/InMemoryStoreInitializer.cs index 82340b1..a122914 100644 --- a/src/Atc.Cosmos.EventStore/InMemory/InMemoryStoreInitializer.cs +++ b/src/Atc.Cosmos.EventStore/InMemory/InMemoryStoreInitializer.cs @@ -1,18 +1,15 @@ -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Microsoft.Azure.Cosmos; -namespace Atc.Cosmos.EventStore.InMemory +namespace Atc.Cosmos.EventStore.InMemory; + +internal class InMemoryStoreInitializer : IEventStoreInitializer { - internal class InMemoryStoreInitializer : IEventStoreInitializer + public void CreateEventStore(ThroughputProperties throughputProperties) { - public void CreateEventStore(ThroughputProperties throughputProperties) - { - // No initialization required for in-memory store. - } - - public Task CreateEventStoreAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken) - => Task.CompletedTask; + // No initialization required for in-memory store. } + + public Task CreateEventStoreAsync(ThroughputProperties throughputProperties, CancellationToken cancellationToken) + => Task.CompletedTask; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IsExternalInit.cs b/src/Atc.Cosmos.EventStore/IsExternalInit.cs index 921098a..6ed8838 100644 --- a/src/Atc.Cosmos.EventStore/IsExternalInit.cs +++ b/src/Atc.Cosmos.EventStore/IsExternalInit.cs @@ -2,16 +2,15 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Atc.Cosmos.EventStore.Cqrs")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("DynamicProxyGenAssembly2")] -namespace System.Runtime.CompilerServices -{ - using System.ComponentModel; +namespace System.Runtime.CompilerServices; + +using System.ComponentModel; - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit - { - } +/// +/// Reserved to be used by the compiler for tracking metadata. +/// This class should not be used by developers in source code. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +internal static class IsExternalInit +{ } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/ProcessEventsHandler.cs b/src/Atc.Cosmos.EventStore/ProcessEventsHandler.cs index e09068a..4ef6595 100644 --- a/src/Atc.Cosmos.EventStore/ProcessEventsHandler.cs +++ b/src/Atc.Cosmos.EventStore/ProcessEventsHandler.cs @@ -1,16 +1,11 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore -{ - /// - /// Delegate for processing events from a subscription. - /// - /// List of events to process. - /// representing request cancellation. - /// A representing the asynchronous operation. - public delegate Task ProcessEventsHandler( - IEnumerable events, - CancellationToken cancellationToken); -} \ No newline at end of file +/// +/// Delegate for processing events from a subscription. +/// +/// List of events to process. +/// representing request cancellation. +/// A representing the asynchronous operation. +public delegate Task ProcessEventsHandler( + IEnumerable events, + CancellationToken cancellationToken); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/ProcessExceptionHandler.cs b/src/Atc.Cosmos.EventStore/ProcessExceptionHandler.cs index b4b0ec2..8b4f28c 100644 --- a/src/Atc.Cosmos.EventStore/ProcessExceptionHandler.cs +++ b/src/Atc.Cosmos.EventStore/ProcessExceptionHandler.cs @@ -1,15 +1,11 @@ -using System; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore -{ - /// - /// Delegate for handling exceptions in a subscription. - /// - /// A unique identifier for the lease. - /// The exception received. - /// A representing the asynchronous operation. - public delegate Task ProcessExceptionHandler( - string leaseToken, - Exception exception); -} \ No newline at end of file +/// +/// Delegate for handling exceptions in a subscription. +/// +/// A unique identifier for the lease. +/// The exception received. +/// A representing the asynchronous operation. +public delegate Task ProcessExceptionHandler( + string leaseToken, + Exception exception); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamClosedException.cs b/src/Atc.Cosmos.EventStore/StreamClosedException.cs index fe1a6ef..116fbbb 100644 --- a/src/Atc.Cosmos.EventStore/StreamClosedException.cs +++ b/src/Atc.Cosmos.EventStore/StreamClosedException.cs @@ -1,29 +1,27 @@ -using System; using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore -{ - /// - /// Exception is throw when attempting to write to a close stream. - /// - [SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] - [SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] - public sealed class StreamClosedException : EventStoreException - { - private const string MessageText = "Stream is closed"; +namespace Atc.Cosmos.EventStore; - public StreamClosedException(StreamId streamId) - : base(MessageText) - { - StreamId = streamId; - } +/// +/// Exception is throw when attempting to write to a close stream. +/// +[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] +[SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] +public sealed class StreamClosedException : EventStoreException +{ + private const string MessageText = "Stream is closed"; - public StreamClosedException(StreamId streamId, Exception innerException) - : base(MessageText, innerException) - { - StreamId = streamId; - } + public StreamClosedException(StreamId streamId) + : base(MessageText) + { + StreamId = streamId; + } - public StreamId StreamId { get; } + public StreamClosedException(StreamId streamId, Exception innerException) + : base(MessageText, innerException) + { + StreamId = streamId; } + + public StreamId StreamId { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamConflictReason.cs b/src/Atc.Cosmos.EventStore/StreamConflictReason.cs index 5b52f5e..2e0dbff 100644 --- a/src/Atc.Cosmos.EventStore/StreamConflictReason.cs +++ b/src/Atc.Cosmos.EventStore/StreamConflictReason.cs @@ -1,20 +1,19 @@ -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public enum StreamConflictReason { - public enum StreamConflictReason - { - /// - /// Stream is expected to be empty but one or more events ware found. - /// - StreamIsNotEmpty, + /// + /// Stream is expected to be empty but one or more events ware found. + /// + StreamIsNotEmpty, - /// - /// Stream is expected to contain events but an empty stream is found. - /// - StreamIsEmpty, + /// + /// Stream is expected to contain events but an empty stream is found. + /// + StreamIsEmpty, - /// - /// Stream is not at the expected version. - /// - ExpectedVersion, - } + /// + /// Stream is not at the expected version. + /// + ExpectedVersion, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamId.cs b/src/Atc.Cosmos.EventStore/StreamId.cs index e54eb86..bd26d11 100644 --- a/src/Atc.Cosmos.EventStore/StreamId.cs +++ b/src/Atc.Cosmos.EventStore/StreamId.cs @@ -1,47 +1,45 @@ -using System; using System.ComponentModel; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public struct StreamId : IEquatable { - public struct StreamId : IEquatable + public StreamId(string streamId) { - public StreamId(string streamId) - { - Value = streamId; - } + Value = streamId; + } - public string Value { get; } + public string Value { get; } - public static implicit operator StreamId(string id) - => new(id); + public static implicit operator StreamId(string id) + => new(id); - public static explicit operator string(StreamId streamId) - => streamId.Value; + public static explicit operator string(StreamId streamId) + => streamId.Value; - public static bool operator ==(StreamId left, StreamId right) - => string.Equals(left.Value, right.Value, StringComparison.Ordinal); + public static bool operator ==(StreamId left, StreamId right) + => string.Equals(left.Value, right.Value, StringComparison.Ordinal); - public static bool operator !=(StreamId left, StreamId right) - => !string.Equals(left.Value, right.Value, StringComparison.Ordinal); + public static bool operator !=(StreamId left, StreamId right) + => !string.Equals(left.Value, right.Value, StringComparison.Ordinal); - public static StreamId ToStreamId(string streamId) - => new(streamId); + public static StreamId ToStreamId(string streamId) + => new(streamId); - public static string FromStreamId(StreamId streamId) - => streamId.Value; + public static string FromStreamId(StreamId streamId) + => streamId.Value; - public static bool Equals(StreamId left, StreamId right) - => left.Equals(right); + public static bool Equals(StreamId left, StreamId right) + => left.Equals(right); - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) - => obj is StreamId id && Equals(id); + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) + => obj is StreamId id && Equals(id); - public bool Equals(StreamId other) - => string.Equals(Value, other.Value, StringComparison.Ordinal); + public bool Equals(StreamId other) + => string.Equals(Value, other.Value, StringComparison.Ordinal); - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - => HashCode.Combine(Value); - } + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + => HashCode.Combine(Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamReadFilter.cs b/src/Atc.Cosmos.EventStore/StreamReadFilter.cs index 9c6060c..33539a8 100644 --- a/src/Atc.Cosmos.EventStore/StreamReadFilter.cs +++ b/src/Atc.Cosmos.EventStore/StreamReadFilter.cs @@ -1,17 +1,14 @@ -using System.Collections.Generic; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore +public class StreamReadFilter { - public class StreamReadFilter - { - /// - /// Gets or sets the type of events to read from the stream. - /// - public IReadOnlyCollection? IncludeEvents { get; set; } + /// + /// Gets or sets the type of events to read from the stream. + /// + public IReadOnlyCollection? IncludeEvents { get; set; } - /// - /// Gets or sets the required version the stream must be at. - /// - public StreamVersion? RequiredVersion { get; set; } - } + /// + /// Gets or sets the required version the stream must be at. + /// + public StreamVersion? RequiredVersion { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamResponse.cs b/src/Atc.Cosmos.EventStore/StreamResponse.cs index e4049d1..8dc7743 100644 --- a/src/Atc.Cosmos.EventStore/StreamResponse.cs +++ b/src/Atc.Cosmos.EventStore/StreamResponse.cs @@ -1,10 +1,7 @@ -using System; +namespace Atc.Cosmos.EventStore; -namespace Atc.Cosmos.EventStore -{ - public record StreamResponse( - StreamId StreamId, - StreamVersion Version, - DateTimeOffset Timestamp, - StreamState State); -} \ No newline at end of file +public record StreamResponse( + StreamId StreamId, + StreamVersion Version, + DateTimeOffset Timestamp, + StreamState State); \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamState.cs b/src/Atc.Cosmos.EventStore/StreamState.cs index 12c82f8..41d5472 100644 --- a/src/Atc.Cosmos.EventStore/StreamState.cs +++ b/src/Atc.Cosmos.EventStore/StreamState.cs @@ -1,20 +1,19 @@ -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public enum StreamState { - public enum StreamState - { - /// - /// Stream is new and contains zero events. - /// - New, + /// + /// Stream is new and contains zero events. + /// + New, - /// - /// Stream is active and contains one or more events. - /// - Active, + /// + /// Stream is active and contains one or more events. + /// + Active, - /// - /// Stream is not longer accepting events as it has been closed. - /// - Closed, - } + /// + /// Stream is not longer accepting events as it has been closed. + /// + Closed, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamVersion.cs b/src/Atc.Cosmos.EventStore/StreamVersion.cs index 9d80f87..6e1fe68 100644 --- a/src/Atc.Cosmos.EventStore/StreamVersion.cs +++ b/src/Atc.Cosmos.EventStore/StreamVersion.cs @@ -1,102 +1,100 @@ -using System; using System.ComponentModel; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +/// +/// Represents a position aka version within an event stream. +/// +public struct StreamVersion : IComparable, IEquatable { /// - /// Represents a position aka version within an event stream. + /// Static representation of the start of a stream. + /// + /// + /// Use this when you require the stream to be empty before writing to it. + /// + public static readonly StreamVersion StartOfStream = new(StartOfStreamValue); + + /// + /// Use this when you want to append events to a stream whether it is empty or not. + /// + public static readonly StreamVersion Any = new(AnyValue); + + /// + /// Represents a stream version that is not empty. /// - public struct StreamVersion : IComparable, IEquatable + /// + /// Use this when you require the stream to contain at least one event before writing to it. + /// + public static readonly StreamVersion NotEmpty = new(NotEmptyValue); + + internal const long NotEmptyValue = -1; + internal const long StartOfStreamValue = 0; + internal const long EndOfStreamValue = long.MaxValue; + internal const long AnyValue = long.MaxValue; + + internal StreamVersion(long version) { - /// - /// Static representation of the start of a stream. - /// - /// - /// Use this when you require the stream to be empty before writing to it. - /// - public static readonly StreamVersion StartOfStream = new(StartOfStreamValue); - - /// - /// Use this when you want to append events to a stream whether it is empty or not. - /// - public static readonly StreamVersion Any = new(AnyValue); - - /// - /// Represents a stream version that is not empty. - /// - /// - /// Use this when you require the stream to contain at least one event before writing to it. - /// - public static readonly StreamVersion NotEmpty = new(NotEmptyValue); - - internal const long NotEmptyValue = -1; - internal const long StartOfStreamValue = 0; - internal const long EndOfStreamValue = long.MaxValue; - internal const long AnyValue = long.MaxValue; - - internal StreamVersion(long version) - { - // Validate value range - Value = version; - } - - /// - /// Gets the value of the stream version. - /// - public long Value { get; } - - public static implicit operator StreamVersion(long version) - => new(version); - - public static explicit operator long(StreamVersion version) - => version.Value; - - public static bool operator >(StreamVersion left, StreamVersion right) - => left.Value > right.Value; - - public static bool operator <=(StreamVersion left, StreamVersion right) - => left.Value <= right.Value; - - public static bool operator >=(StreamVersion left, StreamVersion right) - => left.Value >= right.Value; - - public static bool operator <(StreamVersion left, StreamVersion right) - => left.Value < right.Value; - - public static bool operator ==(StreamVersion left, StreamVersion right) - => left.Value == right.Value; - - public static bool operator !=(StreamVersion left, StreamVersion right) - => left.Value != right.Value; - - public static StreamVersion ToStreamVersion(long version) - => new(version); - - public static long FromStreamVersion(StreamVersion version) - => version.Value; - - /// - /// Compares this instance to a specified and returns an indication of their relative values. - /// - /// A StreamVersion to compare. - /// - /// A value that indicates the relative order of the objects being compared. The - /// return value has these meanings: Value Meaning Less than zero This instance precedes - /// other in the sort order. Zero This instance occurs in the same position in the - /// sort order as other. Greater than zero This instance follows other in the sort order. - /// - public int CompareTo(StreamVersion other) - => Value.CompareTo(other.Value); - - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) - => obj is StreamVersion version && Equals(version); - - public bool Equals(StreamVersion other) - => Value == other.Value; - - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - => HashCode.Combine(Value); + // Validate value range + Value = version; } + + /// + /// Gets the value of the stream version. + /// + public long Value { get; } + + public static implicit operator StreamVersion(long version) + => new(version); + + public static explicit operator long(StreamVersion version) + => version.Value; + + public static bool operator >(StreamVersion left, StreamVersion right) + => left.Value > right.Value; + + public static bool operator <=(StreamVersion left, StreamVersion right) + => left.Value <= right.Value; + + public static bool operator >=(StreamVersion left, StreamVersion right) + => left.Value >= right.Value; + + public static bool operator <(StreamVersion left, StreamVersion right) + => left.Value < right.Value; + + public static bool operator ==(StreamVersion left, StreamVersion right) + => left.Value == right.Value; + + public static bool operator !=(StreamVersion left, StreamVersion right) + => left.Value != right.Value; + + public static StreamVersion ToStreamVersion(long version) + => new(version); + + public static long FromStreamVersion(StreamVersion version) + => version.Value; + + /// + /// Compares this instance to a specified and returns an indication of their relative values. + /// + /// A StreamVersion to compare. + /// + /// A value that indicates the relative order of the objects being compared. The + /// return value has these meanings: Value Meaning Less than zero This instance precedes + /// other in the sort order. Zero This instance occurs in the same position in the + /// sort order as other. Greater than zero This instance follows other in the sort order. + /// + public int CompareTo(StreamVersion other) + => Value.CompareTo(other.Value); + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object? obj) + => obj is StreamVersion version && Equals(version); + + public bool Equals(StreamVersion other) + => Value == other.Value; + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + => HashCode.Combine(Value); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamVersionConflictException.cs b/src/Atc.Cosmos.EventStore/StreamVersionConflictException.cs index d0e3eb6..05f32cc 100644 --- a/src/Atc.Cosmos.EventStore/StreamVersionConflictException.cs +++ b/src/Atc.Cosmos.EventStore/StreamVersionConflictException.cs @@ -1,31 +1,30 @@ using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] +[SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] +public class StreamVersionConflictException : EventStoreException { - [SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] - [SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] - public class StreamVersionConflictException : EventStoreException + public StreamVersionConflictException( + StreamId streamId, + StreamVersion version, + StreamVersion expectedVersion, + StreamConflictReason reason, + string message) + : base(message) { - public StreamVersionConflictException( - StreamId streamId, - StreamVersion version, - StreamVersion expectedVersion, - StreamConflictReason reason, - string message) - : base(message) - { - StreamId = streamId; - Version = version; - ExpectedVersion = expectedVersion; - Reason = reason; - } + StreamId = streamId; + Version = version; + ExpectedVersion = expectedVersion; + Reason = reason; + } - public StreamId StreamId { get; } + public StreamId StreamId { get; } - public StreamVersion Version { get; } + public StreamVersion Version { get; } - public StreamVersion ExpectedVersion { get; } + public StreamVersion ExpectedVersion { get; } - public StreamConflictReason Reason { get; } - } + public StreamConflictReason Reason { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamWriteConflictException.cs b/src/Atc.Cosmos.EventStore/StreamWriteConflictException.cs index fe0eec7..6372172 100644 --- a/src/Atc.Cosmos.EventStore/StreamWriteConflictException.cs +++ b/src/Atc.Cosmos.EventStore/StreamWriteConflictException.cs @@ -1,35 +1,33 @@ -using System; using System.Diagnostics.CodeAnalysis; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +[SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] +[SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] +public class StreamWriteConflictException : EventStoreException { - [SuppressMessage("Design", "CA1032:Implement standard exception constructors", Justification = "By Design")] - [SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "By Design")] - public class StreamWriteConflictException : EventStoreException - { - private const string MessageText = "Stream is not empty"; + private const string MessageText = "Stream is not empty"; - public StreamWriteConflictException(StreamId streamId, StreamVersion version, string message) - : base(message) - { - StreamId = streamId; - Version = version; - } + public StreamWriteConflictException(StreamId streamId, StreamVersion version, string message) + : base(message) + { + StreamId = streamId; + Version = version; + } - public StreamWriteConflictException(StreamId streamId, StreamVersion version) - : this(streamId, version, MessageText) - { - } + public StreamWriteConflictException(StreamId streamId, StreamVersion version) + : this(streamId, version, MessageText) + { + } - public StreamWriteConflictException(StreamId streamId, StreamVersion version, Exception innerException) - : base(MessageText, innerException) - { - StreamId = streamId; - Version = version; - } + public StreamWriteConflictException(StreamId streamId, StreamVersion version, Exception innerException) + : base(MessageText, innerException) + { + StreamId = streamId; + Version = version; + } - public StreamId StreamId { get; } + public StreamId StreamId { get; } - public StreamVersion Version { get; } - } + public StreamVersion Version { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/StreamWriteOptions.cs b/src/Atc.Cosmos.EventStore/StreamWriteOptions.cs index e39f50d..fe21e52 100644 --- a/src/Atc.Cosmos.EventStore/StreamWriteOptions.cs +++ b/src/Atc.Cosmos.EventStore/StreamWriteOptions.cs @@ -1,15 +1,14 @@ -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public class StreamWriteOptions { - public class StreamWriteOptions - { - /// - /// Gets or sets correlation id used to track a request through various systems and services. - /// - public string? CorrelationId { get; set; } + /// + /// Gets or sets correlation id used to track a request through various systems and services. + /// + public string? CorrelationId { get; set; } - /// - /// Gets or sets the id of the event or command that caused the events to be written. - /// - public string? CausationId { get; set; } - } + /// + /// Gets or sets the id of the event or command that caused the events to be written. + /// + public string? CausationId { get; set; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IEventBatchProducer.cs b/src/Atc.Cosmos.EventStore/Streams/IEventBatchProducer.cs index 27cdd4a..15cfa8b 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IEventBatchProducer.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IEventBatchProducer.cs @@ -1,12 +1,9 @@ -using System.Collections.Generic; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IEventBatchProducer { - internal interface IEventBatchProducer - { - StreamBatch FromEvents( - IReadOnlyCollection events, - IStreamMetadata metadata, - StreamWriteOptions? options); - } + StreamBatch FromEvents( + IReadOnlyCollection events, + IStreamMetadata metadata, + StreamWriteOptions? options); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamBatchWriter.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamBatchWriter.cs index 05d06fb..e70abf5 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamBatchWriter.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamBatchWriter.cs @@ -1,12 +1,8 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamBatchWriter { - internal interface IStreamBatchWriter - { - Task WriteAsync( - StreamBatch batch, - CancellationToken cancellationToken); - } + Task WriteAsync( + StreamBatch batch, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointReader.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointReader.cs index a9cd0e9..9ca5d58 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointReader.cs @@ -1,13 +1,9 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamCheckpointReader { - internal interface IStreamCheckpointReader - { - Task?> ReadAsync( - string name, - StreamId streamId, - CancellationToken cancellationToken); - } + Task?> ReadAsync( + string name, + StreamId streamId, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointWriter.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointWriter.cs index 4700840..5d26167 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointWriter.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamCheckpointWriter.cs @@ -1,15 +1,11 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamCheckpointWriter { - internal interface IStreamCheckpointWriter - { - Task WriteAsync( - string name, - StreamId streamId, - StreamVersion streamVersion, - object? state, - CancellationToken cancellationToken); - } + Task WriteAsync( + string name, + StreamId streamId, + StreamVersion streamVersion, + object? state, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamIndexReader.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamIndexReader.cs index e90b10a..9b42eb0 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamIndexReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamIndexReader.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Threading; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamIndexReader { - internal interface IStreamIndexReader - { - IAsyncEnumerable ReadAsync( - string? filter, - DateTimeOffset? createdAfter, - CancellationToken cancellationToken); - } + IAsyncEnumerable ReadAsync( + string? filter, + DateTimeOffset? createdAfter, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamInfoReader.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamInfoReader.cs index 096c1ed..b3d89e9 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamInfoReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamInfoReader.cs @@ -1,12 +1,8 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamInfoReader { - internal interface IStreamInfoReader - { - Task ReadAsync( - StreamId streamId, - CancellationToken cancellationToken = default); - } + Task ReadAsync( + StreamId streamId, + CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamIterator.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamIterator.cs index e19c89b..0bed5d8 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamIterator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamIterator.cs @@ -1,14 +1,10 @@ -using System.Collections.Generic; -using System.Threading; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamIterator { - internal interface IStreamIterator - { - IAsyncEnumerable ReadAsync( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter, - CancellationToken cancellationToken); - } + IAsyncEnumerable ReadAsync( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamMetadataReader.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamMetadataReader.cs index 372c702..79a9226 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamMetadataReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamMetadataReader.cs @@ -1,10 +1,6 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamMetadataReader { - internal interface IStreamMetadataReader - { - Task GetAsync(StreamId streamId, CancellationToken cancellationToken); - } + Task GetAsync(StreamId streamId, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamReadValidator.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamReadValidator.cs index 7ca9324..e45de3a 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamReadValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamReadValidator.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +public interface IStreamReadValidator { - public interface IStreamReadValidator - { - void Validate(IStreamMetadata metadata, StreamVersion version); - } + void Validate(IStreamMetadata metadata, StreamVersion version); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamReader.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamReader.cs index 5828729..6cc8b07 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamReader.cs @@ -1,14 +1,10 @@ -using System.Collections.Generic; -using System.Threading; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamReader { - internal interface IStreamReader - { - IAsyncEnumerable ReadAsync( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter, - CancellationToken cancellationToken); - } + IAsyncEnumerable ReadAsync( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionFactory.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionFactory.cs index bc97bd6..31a87a7 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionFactory.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionFactory.cs @@ -1,11 +1,10 @@ -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal interface IStreamSubscriptionFactory { - internal interface IStreamSubscriptionFactory - { - IStreamSubscription Create( - ConsumerGroup consumerGroup, - SubscriptionStartOptions startOptions, - ProcessEventsHandler eventsHandler, - ProcessExceptionHandler exceptionHandler); - } + IStreamSubscription Create( + ConsumerGroup consumerGroup, + SubscriptionStartOptions startOptions, + ProcessEventsHandler eventsHandler, + ProcessExceptionHandler exceptionHandler); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionRemover.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionRemover.cs index 452ce70..9efa6f7 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionRemover.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamSubscriptionRemover.cs @@ -1,12 +1,8 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamSubscriptionRemover { - internal interface IStreamSubscriptionRemover - { - Task DeleteAsync( - ConsumerGroup consumerGroup, - CancellationToken cancellationToken); - } + Task DeleteAsync( + ConsumerGroup consumerGroup, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamValidator.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamValidator.cs index 19fa8e7..dd8664c 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamValidator.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal interface IStreamValidator { - internal interface IStreamValidator - { - void Validate(IStreamMetadata metadata, StreamVersion version); - } + void Validate(IStreamMetadata metadata, StreamVersion version); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamWriteValidator.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamWriteValidator.cs index 23c509f..41fd698 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamWriteValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamWriteValidator.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal interface IStreamWriteValidator { - internal interface IStreamWriteValidator - { - void Validate(IStreamMetadata metadata, StreamVersion version); - } + void Validate(IStreamMetadata metadata, StreamVersion version); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/IStreamWriter.cs b/src/Atc.Cosmos.EventStore/Streams/IStreamWriter.cs index 3466b2d..2249208 100644 --- a/src/Atc.Cosmos.EventStore/Streams/IStreamWriter.cs +++ b/src/Atc.Cosmos.EventStore/Streams/IStreamWriter.cs @@ -1,16 +1,11 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal interface IStreamWriter { - internal interface IStreamWriter - { - Task WriteAsync( - StreamId streamId, - IReadOnlyCollection events, - StreamVersion version, - StreamWriteOptions? options, - CancellationToken cancellationToken); - } + Task WriteAsync( + StreamId streamId, + IReadOnlyCollection events, + StreamVersion version, + StreamWriteOptions? options, + CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamBatch.cs b/src/Atc.Cosmos.EventStore/Streams/StreamBatch.cs index 18548a8..92285cf 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamBatch.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamBatch.cs @@ -1,20 +1,18 @@ -using System.Collections.Generic; using Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamBatch { - internal class StreamBatch + public StreamBatch( + StreamMetadata metadata, + IReadOnlyCollection events) { - public StreamBatch( - StreamMetadata metadata, - IReadOnlyCollection events) - { - Metadata = metadata; - Documents = events; - } + Metadata = metadata; + Documents = events; + } - public StreamMetadata Metadata { get; } + public StreamMetadata Metadata { get; } - public IReadOnlyCollection Documents { get; } - } + public IReadOnlyCollection Documents { get; } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamIndex.cs b/src/Atc.Cosmos.EventStore/Streams/StreamIndex.cs index 50de9df..888b163 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamIndex.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamIndex.cs @@ -1,18 +1,16 @@ -using System; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamIndex : IStreamIndex { - internal class StreamIndex : IStreamIndex - { - [JsonPropertyName("id")] - public StreamId StreamId { get; set; } = string.Empty; + [JsonPropertyName("id")] + public StreamId StreamId { get; set; } = string.Empty; - [JsonPropertyName("pk")] - public string PartitionKey { get; set; } = nameof(StreamIndex); + [JsonPropertyName("pk")] + public string PartitionKey { get; set; } = nameof(StreamIndex); - public DateTimeOffset Timestamp { get; set; } + public DateTimeOffset Timestamp { get; set; } - public bool IsActive { get; set; } = true; - } + public bool IsActive { get; set; } = true; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamInfoReader.cs b/src/Atc.Cosmos.EventStore/Streams/StreamInfoReader.cs index cb3de2b..6cd6782 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamInfoReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamInfoReader.cs @@ -1,19 +1,15 @@ -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal class StreamInfoReader : IStreamInfoReader { - internal class StreamInfoReader : IStreamInfoReader - { - private readonly IStreamMetadataReader metadataReader; + private readonly IStreamMetadataReader metadataReader; - public StreamInfoReader( - IStreamMetadataReader metadataReader) - => this.metadataReader = metadataReader; + public StreamInfoReader( + IStreamMetadataReader metadataReader) + => this.metadataReader = metadataReader; - public Task ReadAsync( - StreamId streamId, - CancellationToken cancellationToken = default) - => metadataReader.GetAsync(streamId, cancellationToken); - } + public Task ReadAsync( + StreamId streamId, + CancellationToken cancellationToken = default) + => metadataReader.GetAsync(streamId, cancellationToken); } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamMetadata.cs b/src/Atc.Cosmos.EventStore/Streams/StreamMetadata.cs index d5838dc..bf11c98 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamMetadata.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamMetadata.cs @@ -1,43 +1,41 @@ -using System; using System.Text.Json.Serialization; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamMetadata : IStreamMetadata { - internal class StreamMetadata : IStreamMetadata + public const string StreamMetadataId = "meta-data"; + + public StreamMetadata( + string id, + string partitionKey, + StreamId streamId, + StreamVersion version, + StreamState state, + DateTimeOffset timestamp) { - public const string StreamMetadataId = "meta-data"; - - public StreamMetadata( - string id, - string partitionKey, - StreamId streamId, - StreamVersion version, - StreamState state, - DateTimeOffset timestamp) - { - Id = id; - PartitionKey = partitionKey; - StreamId = streamId; - Version = version; - State = state; - Timestamp = timestamp; - } + Id = id; + PartitionKey = partitionKey; + StreamId = streamId; + Version = version; + State = state; + Timestamp = timestamp; + } - [JsonPropertyName("id")] - public string Id { get; set; } + [JsonPropertyName("id")] + public string Id { get; set; } - [JsonPropertyName("pk")] - public string PartitionKey { get; set; } + [JsonPropertyName("pk")] + public string PartitionKey { get; set; } - public StreamId StreamId { get; set; } + public StreamId StreamId { get; set; } - public StreamVersion Version { get; set; } + public StreamVersion Version { get; set; } - public StreamState State { get; set; } + public StreamState State { get; set; } - public DateTimeOffset Timestamp { get; set; } + public DateTimeOffset Timestamp { get; set; } - [JsonIgnore] - public string ETag { get; set; } = string.Empty; - } + [JsonIgnore] + public string ETag { get; set; } = string.Empty; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamReadValidator.cs b/src/Atc.Cosmos.EventStore/Streams/StreamReadValidator.cs index c9ad31d..68235e2 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamReadValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamReadValidator.cs @@ -1,20 +1,18 @@ -using System; using Atc.Cosmos.EventStore.Streams.Validators; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamReadValidator : IStreamReadValidator { - internal class StreamReadValidator : IStreamReadValidator + private static readonly IStreamValidator[] Validators = new IStreamValidator[] { - private static readonly IStreamValidator[] Validators = new IStreamValidator[] - { - new StreamNotEmptyValidator(), - new StreamEmptyValidator(), - new StreamExpectedVersionValidator(), - }; + new StreamNotEmptyValidator(), + new StreamEmptyValidator(), + new StreamExpectedVersionValidator(), + }; - public void Validate(IStreamMetadata metadata, StreamVersion version) - => Array.ForEach( - Validators, - v => v.Validate(metadata, version)); - } + public void Validate(IStreamMetadata metadata, StreamVersion version) + => Array.ForEach( + Validators, + v => v.Validate(metadata, version)); } diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamReader.cs b/src/Atc.Cosmos.EventStore/Streams/StreamReader.cs index fdab986..5d26751 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamReader.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamReader.cs @@ -1,52 +1,48 @@ -using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamReader : IStreamReader { - internal class StreamReader : IStreamReader + private readonly IStreamMetadataReader metadataReader; + private readonly IStreamReadValidator readValidator; + private readonly IStreamIterator streamIterator; + + public StreamReader( + IStreamMetadataReader metadataReader, + IStreamReadValidator readValidator, + IStreamIterator streamIterator) + { + this.metadataReader = metadataReader; + this.readValidator = readValidator; + this.streamIterator = streamIterator; + } + + public async IAsyncEnumerable ReadAsync( + StreamId streamId, + StreamVersion fromVersion, + StreamReadFilter? filter, + [EnumeratorCancellation] CancellationToken cancellationToken) { - private readonly IStreamMetadataReader metadataReader; - private readonly IStreamReadValidator readValidator; - private readonly IStreamIterator streamIterator; + var metadata = await metadataReader + .GetAsync(streamId, cancellationToken) + .ConfigureAwait(false); - public StreamReader( - IStreamMetadataReader metadataReader, - IStreamReadValidator readValidator, - IStreamIterator streamIterator) + readValidator.Validate( + metadata, + filter?.RequiredVersion ?? StreamVersion.Any); + + // If we don't have any events in the stream, then skip reading from stream. + if (metadata.Version == 0) { - this.metadataReader = metadataReader; - this.readValidator = readValidator; - this.streamIterator = streamIterator; + yield break; } - public async IAsyncEnumerable ReadAsync( - StreamId streamId, - StreamVersion fromVersion, - StreamReadFilter? filter, - [EnumeratorCancellation] CancellationToken cancellationToken) + await foreach (var evt in streamIterator + .ReadAsync(streamId, fromVersion, filter, cancellationToken) + .ConfigureAwait(false)) { - var metadata = await metadataReader - .GetAsync(streamId, cancellationToken) - .ConfigureAwait(false); - - readValidator.Validate( - metadata, - filter?.RequiredVersion ?? StreamVersion.Any); - - // If we don't have any events in the stream, then skip reading from stream. - if (metadata.Version == 0) - { - yield break; - } - - await foreach (var evt in streamIterator - .ReadAsync(streamId, fromVersion, filter, cancellationToken) - .ConfigureAwait(false)) - { - yield return evt; - } + yield return evt; } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamWriteValidator.cs b/src/Atc.Cosmos.EventStore/Streams/StreamWriteValidator.cs index 215252f..f8e5f96 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamWriteValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamWriteValidator.cs @@ -1,21 +1,19 @@ -using System; using Atc.Cosmos.EventStore.Streams.Validators; -namespace Atc.Cosmos.EventStore.Streams +namespace Atc.Cosmos.EventStore.Streams; + +internal class StreamWriteValidator : IStreamWriteValidator { - internal class StreamWriteValidator : IStreamWriteValidator + private static readonly IStreamValidator[] Validators = new IStreamValidator[] { - private static readonly IStreamValidator[] Validators = new IStreamValidator[] - { - new StreamClosedValidator(), - new StreamEmptyValidator(), - new StreamNotEmptyValidator(), - new StreamExpectedVersionValidator(), - }; + new StreamClosedValidator(), + new StreamEmptyValidator(), + new StreamNotEmptyValidator(), + new StreamExpectedVersionValidator(), + }; - public void Validate(IStreamMetadata metadata, StreamVersion version) - => Array.ForEach( - Validators, - v => v.Validate(metadata, version)); - } + public void Validate(IStreamMetadata metadata, StreamVersion version) + => Array.ForEach( + Validators, + v => v.Validate(metadata, version)); } diff --git a/src/Atc.Cosmos.EventStore/Streams/StreamWriter.cs b/src/Atc.Cosmos.EventStore/Streams/StreamWriter.cs index 585f017..e6175b0 100644 --- a/src/Atc.Cosmos.EventStore/Streams/StreamWriter.cs +++ b/src/Atc.Cosmos.EventStore/Streams/StreamWriter.cs @@ -1,53 +1,48 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Streams; -namespace Atc.Cosmos.EventStore.Streams +internal class StreamWriter : IStreamWriter { - internal class StreamWriter : IStreamWriter - { - private readonly IStreamMetadataReader metadataReader; - private readonly IStreamWriteValidator validator; - private readonly IEventBatchProducer batchProducer; - private readonly IStreamBatchWriter batchWriter; + private readonly IStreamMetadataReader metadataReader; + private readonly IStreamWriteValidator validator; + private readonly IEventBatchProducer batchProducer; + private readonly IStreamBatchWriter batchWriter; - public StreamWriter( - IStreamMetadataReader metadataReader, - IStreamWriteValidator validator, - IEventBatchProducer batchProducer, - IStreamBatchWriter batchWriter) - { - this.metadataReader = metadataReader; - this.validator = validator; - this.batchProducer = batchProducer; - this.batchWriter = batchWriter; - } + public StreamWriter( + IStreamMetadataReader metadataReader, + IStreamWriteValidator validator, + IEventBatchProducer batchProducer, + IStreamBatchWriter batchWriter) + { + this.metadataReader = metadataReader; + this.validator = validator; + this.batchProducer = batchProducer; + this.batchWriter = batchWriter; + } - public async Task WriteAsync( - StreamId streamId, - IReadOnlyCollection events, - StreamVersion version, - StreamWriteOptions? options, - CancellationToken cancellationToken) - { - var metadata = await metadataReader - .GetAsync(streamId, cancellationToken) - .ConfigureAwait(false); + public async Task WriteAsync( + StreamId streamId, + IReadOnlyCollection events, + StreamVersion version, + StreamWriteOptions? options, + CancellationToken cancellationToken) + { + var metadata = await metadataReader + .GetAsync(streamId, cancellationToken) + .ConfigureAwait(false); - validator.Validate(metadata, version); + validator.Validate(metadata, version); - var batch = batchProducer - .FromEvents(events, metadata, options); + var batch = batchProducer + .FromEvents(events, metadata, options); - var response = await batchWriter - .WriteAsync(batch, cancellationToken) - .ConfigureAwait(false); + var response = await batchWriter + .WriteAsync(batch, cancellationToken) + .ConfigureAwait(false); - return new StreamResponse( - response.StreamId, - response.Version, - response.Timestamp, - response.State); - } + return new StreamResponse( + response.StreamId, + response.Version, + response.Timestamp, + response.State); } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamClosedValidator.cs b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamClosedValidator.cs index e6ef19a..c3e70d6 100644 --- a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamClosedValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamClosedValidator.cs @@ -1,13 +1,12 @@ -namespace Atc.Cosmos.EventStore.Streams.Validators +namespace Atc.Cosmos.EventStore.Streams.Validators; + +internal class StreamClosedValidator : IStreamValidator { - internal class StreamClosedValidator : IStreamValidator + public void Validate(IStreamMetadata metadata, StreamVersion version) { - public void Validate(IStreamMetadata metadata, StreamVersion version) + if (metadata.State == StreamState.Closed) { - if (metadata.State == StreamState.Closed) - { - throw new StreamClosedException(metadata.StreamId); - } + throw new StreamClosedException(metadata.StreamId); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamEmptyValidator.cs b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamEmptyValidator.cs index 0cc3fcf..e378ce8 100644 --- a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamEmptyValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamEmptyValidator.cs @@ -1,27 +1,26 @@ -namespace Atc.Cosmos.EventStore.Streams.Validators +namespace Atc.Cosmos.EventStore.Streams.Validators; + +/// +/// Responsible for validating if the stream is required to be empty and if not then throw . +/// +internal class StreamEmptyValidator : IStreamValidator { - /// - /// Responsible for validating if the stream is required to be empty and if not then throw . - /// - internal class StreamEmptyValidator : IStreamValidator + public void Validate(IStreamMetadata metadata, StreamVersion version) { - public void Validate(IStreamMetadata metadata, StreamVersion version) + // Pass validation if the required version is not start of stream. + if (version != StreamVersion.StartOfStream) { - // Pass validation if the required version is not start of stream. - if (version != StreamVersion.StartOfStream) - { - return; - } + return; + } - if (metadata.Version != StreamVersion.StartOfStream) - { - throw new StreamVersionConflictException( - metadata.StreamId, - metadata.Version, - version, - StreamConflictReason.StreamIsNotEmpty, - "Stream is expected to be empty."); - } + if (metadata.Version != StreamVersion.StartOfStream) + { + throw new StreamVersionConflictException( + metadata.StreamId, + metadata.Version, + version, + StreamConflictReason.StreamIsNotEmpty, + "Stream is expected to be empty."); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamExpectedVersionValidator.cs b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamExpectedVersionValidator.cs index fae2438..b6c2413 100644 --- a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamExpectedVersionValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamExpectedVersionValidator.cs @@ -1,27 +1,26 @@ -namespace Atc.Cosmos.EventStore.Streams.Validators +namespace Atc.Cosmos.EventStore.Streams.Validators; + +/// +/// Responsible for validating if a stream is at an expected version and throw if not. +/// +internal class StreamExpectedVersionValidator : IStreamValidator { - /// - /// Responsible for validating if a stream is at an expected version and throw if not. - /// - internal class StreamExpectedVersionValidator : IStreamValidator + public void Validate(IStreamMetadata metadata, StreamVersion version) { - public void Validate(IStreamMetadata metadata, StreamVersion version) + if (version == StreamVersion.Any || version == StreamVersion.NotEmpty) { - if (version == StreamVersion.Any || version == StreamVersion.NotEmpty) - { - return; - } + return; + } - // If the stream version is not equal to the required version required, then throw. - if (metadata.Version != version) - { - throw new StreamVersionConflictException( - metadata.StreamId, - metadata.Version, - version, - StreamConflictReason.ExpectedVersion, - $"Stream version was expected to be {version.Value} but is at {metadata.Version.Value}."); - } + // If the stream version is not equal to the required version required, then throw. + if (metadata.Version != version) + { + throw new StreamVersionConflictException( + metadata.StreamId, + metadata.Version, + version, + StreamConflictReason.ExpectedVersion, + $"Stream version was expected to be {version.Value} but is at {metadata.Version.Value}."); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs index f39c8d8..82fb354 100644 --- a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs @@ -1,27 +1,26 @@ -namespace Atc.Cosmos.EventStore.Streams.Validators +namespace Atc.Cosmos.EventStore.Streams.Validators; + +/// +/// Responsible for validating if the stream is required to contain events and if not then throw . +/// +internal class StreamNotEmptyValidator : IStreamValidator { - /// - /// Responsible for validating if the stream is required to contain events and if not then throw . - /// - internal class StreamNotEmptyValidator : IStreamValidator + public void Validate(IStreamMetadata metadata, StreamVersion version) { - public void Validate(IStreamMetadata metadata, StreamVersion version) + // Pass validation if the required version is not NotEmpty. + if (version != StreamVersion.NotEmpty) { - // Pass validation if the required version is not NotEmpty. - if (version != StreamVersion.NotEmpty) - { - return; - } + return; + } - if (metadata.Version <= StreamVersion.StartOfStream) - { - throw new StreamVersionConflictException( - metadata.StreamId, - metadata.Version, - version, - StreamConflictReason.StreamIsEmpty, - $"Stream is expected to be empty but found {metadata.Version} events."); - } + if (metadata.Version <= StreamVersion.StartOfStream) + { + throw new StreamVersionConflictException( + metadata.StreamId, + metadata.Version, + version, + StreamConflictReason.StreamIsEmpty, + $"Stream is expected to be empty but found {metadata.Version} events."); } } } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/SubscriptionStartOptions.cs b/src/Atc.Cosmos.EventStore/SubscriptionStartOptions.cs index 03c8ddb..42cac37 100644 --- a/src/Atc.Cosmos.EventStore/SubscriptionStartOptions.cs +++ b/src/Atc.Cosmos.EventStore/SubscriptionStartOptions.cs @@ -1,21 +1,20 @@ -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public enum SubscriptionStartOptions { - public enum SubscriptionStartOptions - { - /// - /// The first time the subscription is started, start receiving changes from the beginning of time. - /// - /// - /// If the subscription has previous been started, it will resume from last checkpoint. - /// - FromBegining, + /// + /// The first time the subscription is started, start receiving changes from the beginning of time. + /// + /// + /// If the subscription has previous been started, it will resume from last checkpoint. + /// + FromBegining, - /// - /// The first time the subscription is started, start receive changes from now. - /// - /// - /// If the subscription has previous been started, it will resume from last checkpoint. - /// - FromNow, - } + /// + /// The first time the subscription is started, start receive changes from now. + /// + /// + /// If the subscription has previous been started, it will resume from last checkpoint. + /// + FromNow, } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/UtcDateTimeProvider.cs b/src/Atc.Cosmos.EventStore/UtcDateTimeProvider.cs index c0c705f..c8b490e 100644 --- a/src/Atc.Cosmos.EventStore/UtcDateTimeProvider.cs +++ b/src/Atc.Cosmos.EventStore/UtcDateTimeProvider.cs @@ -1,11 +1,9 @@ -using System; using Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore +namespace Atc.Cosmos.EventStore; + +public class UtcDateTimeProvider : IDateTimeProvider { - public class UtcDateTimeProvider : IDateTimeProvider - { - public DateTimeOffset GetDateTime() - => DateTimeOffset.UtcNow; - } + public DateTimeOffset GetDateTime() + => DateTimeOffset.UtcNow; } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/DependencyInjection/Internal/ProjectionBuilderTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/DependencyInjection/Internal/ProjectionBuilderTests.cs index cb440d8..44b71f8 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/DependencyInjection/Internal/ProjectionBuilderTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/DependencyInjection/Internal/ProjectionBuilderTests.cs @@ -1,4 +1,3 @@ -using System; using Atc.Cosmos.EventStore.Cqrs.DependencyInjection.Internal; using Atc.Cosmos.EventStore.Cqrs.Projections; using Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; @@ -6,51 +5,50 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Cqrs.Tests.DependencyInjection.Internal +namespace Atc.Cosmos.EventStore.Cqrs.Tests.DependencyInjection.Internal; + +public class ProjectionBuilderTests { - public class ProjectionBuilderTests + [Theory, AutoNSubstituteData] + internal void Should_Set_Name( + string name, + ProjectionOptions options, + ProjectionBuilder sut) + { + sut.WithJobName(name); + sut.Build(options); + + options.Name.Should().Be(name); + } + + [Theory, AutoNSubstituteData] + internal void ShouldThrow_When_Projection_IsMissing_ProjectionFilter( + ProjectionOptions options, + ProjectionBuilder sut) + => FluentActions + .Invoking(() => sut.Build(options)) + .Should() + .ThrowExactly(); + + [Theory, AutoNSubstituteData] + internal void Should_Set_ExceptionHandler( + ProcessExceptionHandler handler, + ProjectionOptions options, + ProjectionBuilder sut) { - [Theory, AutoNSubstituteData] - internal void Should_Set_Name( - string name, - ProjectionOptions options, - ProjectionBuilder sut) - { - sut.WithJobName(name); - sut.Build(options); - - options.Name.Should().Be(name); - } - - [Theory, AutoNSubstituteData] - internal void ShouldThrow_When_Projection_IsMissing_ProjectionFilter( - ProjectionOptions options, - ProjectionBuilder sut) - => FluentActions - .Invoking(() => sut.Build(options)) - .Should() - .ThrowExactly(); - - [Theory, AutoNSubstituteData] - internal void Should_Set_ExceptionHandler( - ProcessExceptionHandler handler, - ProjectionOptions options, - ProjectionBuilder sut) - { - sut.WithExceptionHandler(handler); - sut.Build(options); - - options.ExceptionHandler.Should().Be(handler); - } - - [Theory, AutoNSubstituteData] - internal void ShouldHave_Default_ExceptionHandler( - ProjectionOptions options, - ProjectionBuilder sut) - { - sut.Build(options); - - options.ExceptionHandler.Should().NotBeNull(); - } + sut.WithExceptionHandler(handler); + sut.Build(options); + + options.ExceptionHandler.Should().Be(handler); + } + + [Theory, AutoNSubstituteData] + internal void ShouldHave_Default_ExceptionHandler( + ProjectionOptions options, + ProjectionBuilder sut) + { + sut.Build(options); + + options.ExceptionHandler.Should().NotBeNull(); } } diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs index 3dfe2fb..ae0e2cd 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs @@ -1,24 +1,19 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; -namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks +[ProjectionFilter("**")] +internal class TestProjection : IProjection { - [ProjectionFilter("**")] - internal class TestProjection : IProjection - { - public Task CompleteAsync( - CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task CompleteAsync( + CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task FailedAsync( - Exception exception, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task FailedAsync( + Exception exception, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task InitializeAsync( - EventStreamId id, - CancellationToken cancellationToken) - => throw new NotImplementedException(); - } + public Task InitializeAsync( + EventStreamId id, + CancellationToken cancellationToken) + => throw new NotImplementedException(); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs index d84f448..918b914 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs @@ -1,23 +1,18 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; -namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks +internal class TestProjectionMissingFilterAttribute : IProjection { - internal class TestProjectionMissingFilterAttribute : IProjection - { - public Task CompleteAsync( - CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task CompleteAsync( + CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task FailedAsync( - Exception exception, - CancellationToken cancellationToken) - => throw new NotImplementedException(); + public Task FailedAsync( + Exception exception, + CancellationToken cancellationToken) + => throw new NotImplementedException(); - public Task InitializeAsync( - EventStreamId id, - CancellationToken cancellationToken) - => throw new NotImplementedException(); - } + public Task InitializeAsync( + EventStreamId id, + CancellationToken cancellationToken) + => throw new NotImplementedException(); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Projections/ProjectionFilterTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Projections/ProjectionFilterTests.cs index 0fd06c4..0136925 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Projections/ProjectionFilterTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Projections/ProjectionFilterTests.cs @@ -3,76 +3,75 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Cqrs.Tests.Projections +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Projections; + +public class ProjectionFilterTests { - public class ProjectionFilterTests - { - [Theory] - [InlineAutoNSubstituteData("*", "type.1", false)] - [InlineAutoNSubstituteData("*", "type.1.2", false)] - [InlineAutoNSubstituteData("*", "type.1.2.3", false)] - [InlineAutoNSubstituteData("*", "type.1.2.3.4", false)] - [InlineAutoNSubstituteData("*", "type.1.2.3.4.5", false)] - [InlineAutoNSubstituteData("*", "type", true)] - [InlineAutoNSubstituteData("type.*", "type.1", true)] - [InlineAutoNSubstituteData("type.*", "type.1.2", false)] - [InlineAutoNSubstituteData("type.*", "type.1.2.3", false)] - [InlineAutoNSubstituteData("type.*", "type.1.2.3.4", false)] - [InlineAutoNSubstituteData("type.*", "type.1.2.3.4.5", false)] - [InlineAutoNSubstituteData("type.*", "type", false)] - [InlineAutoNSubstituteData("type.*.2", "type.1", false)] - [InlineAutoNSubstituteData("type.*.2", "type.1.2", true)] - [InlineAutoNSubstituteData("type.*.2", "type.1.2.3", false)] - [InlineAutoNSubstituteData("type.*.2", "type.1.2.3.4", false)] - [InlineAutoNSubstituteData("type.*.2", "type.1.2.3.4.5", false)] - [InlineAutoNSubstituteData("type.*.*", "type", false)] - [InlineAutoNSubstituteData("type.*.*", "type.1", false)] - [InlineAutoNSubstituteData("type.*.*", "type.1.2", true)] - [InlineAutoNSubstituteData("type.*.*", "type.1.2.3", false)] - [InlineAutoNSubstituteData("type.*.*", "type.1.2.3.4", false)] - [InlineAutoNSubstituteData("type.*.*", "type.1.2.3.4.5", false)] - [InlineAutoNSubstituteData("type.*.*", "type", false)] - public void Should_Evaluate_Using_One_Wildcard( - string filter, - string streamId, - bool pass) - => new ProjectionFilter(filter) - .Evaluate(streamId) - .Should() - .Be(pass); + [Theory] + [InlineAutoNSubstituteData("*", "type.1", false)] + [InlineAutoNSubstituteData("*", "type.1.2", false)] + [InlineAutoNSubstituteData("*", "type.1.2.3", false)] + [InlineAutoNSubstituteData("*", "type.1.2.3.4", false)] + [InlineAutoNSubstituteData("*", "type.1.2.3.4.5", false)] + [InlineAutoNSubstituteData("*", "type", true)] + [InlineAutoNSubstituteData("type.*", "type.1", true)] + [InlineAutoNSubstituteData("type.*", "type.1.2", false)] + [InlineAutoNSubstituteData("type.*", "type.1.2.3", false)] + [InlineAutoNSubstituteData("type.*", "type.1.2.3.4", false)] + [InlineAutoNSubstituteData("type.*", "type.1.2.3.4.5", false)] + [InlineAutoNSubstituteData("type.*", "type", false)] + [InlineAutoNSubstituteData("type.*.2", "type.1", false)] + [InlineAutoNSubstituteData("type.*.2", "type.1.2", true)] + [InlineAutoNSubstituteData("type.*.2", "type.1.2.3", false)] + [InlineAutoNSubstituteData("type.*.2", "type.1.2.3.4", false)] + [InlineAutoNSubstituteData("type.*.2", "type.1.2.3.4.5", false)] + [InlineAutoNSubstituteData("type.*.*", "type", false)] + [InlineAutoNSubstituteData("type.*.*", "type.1", false)] + [InlineAutoNSubstituteData("type.*.*", "type.1.2", true)] + [InlineAutoNSubstituteData("type.*.*", "type.1.2.3", false)] + [InlineAutoNSubstituteData("type.*.*", "type.1.2.3.4", false)] + [InlineAutoNSubstituteData("type.*.*", "type.1.2.3.4.5", false)] + [InlineAutoNSubstituteData("type.*.*", "type", false)] + public void Should_Evaluate_Using_One_Wildcard( + string filter, + string streamId, + bool pass) + => new ProjectionFilter(filter) + .Evaluate(streamId) + .Should() + .Be(pass); - [Theory] - [InlineAutoNSubstituteData("**", "type.1", true)] - [InlineAutoNSubstituteData("**", "type.1.2", true)] - [InlineAutoNSubstituteData("**", "type.1.2.3", true)] - [InlineAutoNSubstituteData("**", "type.1.2.3.4", true)] - [InlineAutoNSubstituteData("**", "type.1.2.3.4.5", true)] - [InlineAutoNSubstituteData("**", "type", true)] - [InlineAutoNSubstituteData("type.**", "type.1", true)] - [InlineAutoNSubstituteData("type.**", "type.1.2", true)] - [InlineAutoNSubstituteData("type.**", "type.1.2.3", true)] - [InlineAutoNSubstituteData("type.**", "type.1.2.3.4", true)] - [InlineAutoNSubstituteData("type.**", "type.1.2.3.4.5", true)] - [InlineAutoNSubstituteData("type.**", "type", false)] - [InlineAutoNSubstituteData("type.*.**", "type.1", false)] - [InlineAutoNSubstituteData("type.*.**", "type.1.2", true)] - [InlineAutoNSubstituteData("type.*.**", "type.1.2.3", true)] - [InlineAutoNSubstituteData("type.*.**", "type.1.2.3.4", true)] - [InlineAutoNSubstituteData("type.*.**", "type.1.2.3.4.5", true)] - [InlineAutoNSubstituteData("type.*.*.**", "type", false)] - [InlineAutoNSubstituteData("type.*.*.**", "type.1", false)] - [InlineAutoNSubstituteData("type.*.*.**", "type.1.2", false)] - [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3", true)] - [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3.4", true)] - [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3.4.5", true)] - [InlineAutoNSubstituteData("type.*.*.**", "type", false)] - public void Should_Evaluate_All( - string filter, - string streamId, - bool pass) - => new ProjectionFilter(filter) - .Evaluate(streamId) - .Should() - .Be(pass); - } + [Theory] + [InlineAutoNSubstituteData("**", "type.1", true)] + [InlineAutoNSubstituteData("**", "type.1.2", true)] + [InlineAutoNSubstituteData("**", "type.1.2.3", true)] + [InlineAutoNSubstituteData("**", "type.1.2.3.4", true)] + [InlineAutoNSubstituteData("**", "type.1.2.3.4.5", true)] + [InlineAutoNSubstituteData("**", "type", true)] + [InlineAutoNSubstituteData("type.**", "type.1", true)] + [InlineAutoNSubstituteData("type.**", "type.1.2", true)] + [InlineAutoNSubstituteData("type.**", "type.1.2.3", true)] + [InlineAutoNSubstituteData("type.**", "type.1.2.3.4", true)] + [InlineAutoNSubstituteData("type.**", "type.1.2.3.4.5", true)] + [InlineAutoNSubstituteData("type.**", "type", false)] + [InlineAutoNSubstituteData("type.*.**", "type.1", false)] + [InlineAutoNSubstituteData("type.*.**", "type.1.2", true)] + [InlineAutoNSubstituteData("type.*.**", "type.1.2.3", true)] + [InlineAutoNSubstituteData("type.*.**", "type.1.2.3.4", true)] + [InlineAutoNSubstituteData("type.*.**", "type.1.2.3.4.5", true)] + [InlineAutoNSubstituteData("type.*.*.**", "type", false)] + [InlineAutoNSubstituteData("type.*.*.**", "type.1", false)] + [InlineAutoNSubstituteData("type.*.*.**", "type.1.2", false)] + [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3", true)] + [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3.4", true)] + [InlineAutoNSubstituteData("type.*.*.**", "type.1.2.3.4.5", true)] + [InlineAutoNSubstituteData("type.*.*.**", "type", false)] + public void Should_Evaluate_All( + string filter, + string streamId, + bool pass) + => new ProjectionFilter(filter) + .Evaluate(streamId) + .Should() + .Be(pass); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEvent.cs b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEvent.cs index 60e0522..ec05f21 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEvent.cs +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEvent.cs @@ -1,7 +1,6 @@ -namespace Atc.Cosmos.EventStore.IntegrationTests +namespace Atc.Cosmos.EventStore.IntegrationTests; + +public class SampleEvent { - public class SampleEvent - { - public string Name { get; set; } - } + public string Name { get; set; } } diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs index 96bd994..7244bcf 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/SampleEventTypeProvider.cs @@ -1,10 +1,8 @@ -using System; using Atc.Cosmos.EventStore.Events; -namespace Atc.Cosmos.EventStore.IntegrationTests +namespace Atc.Cosmos.EventStore.IntegrationTests; + +public class SampleEventTypeProvider : IEventTypeProvider { - public class SampleEventTypeProvider : IEventTypeProvider - { - public Type? GetEventType(EventName name) => typeof(SampleEvent); - } + public Type? GetEventType(EventName name) => typeof(SampleEvent); } diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs index eebf34b..a66c37e 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/EventDataConverterPipelineBuilderTests.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Text.Json; using Atc.Cosmos.EventStore.Converters; using Atc.Cosmos.EventStore.Tests.Fakes; diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs index fed40bd..274b85f 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/FaultedEventDataConverterTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Text.Json; using Atc.Cosmos.EventStore.Converters; using Atc.Cosmos.EventStore.Tests.Fakes; @@ -7,43 +5,42 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Converters +namespace Atc.Cosmos.EventStore.Tests.Converters; + +public class FaultedEventDataConverterTests { - public class FaultedEventDataConverterTests - { - private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); - [Theory, AutoNSubstituteData] - internal void Should_Return_Converted_Value( - IEventMetadata metadata, - JsonSerializerOptions options, - string expected, - FaultedEventDataConverter sut) - => sut - .Convert( - metadata, - doc.RootElement, - options, - () => expected) - .Should() - .Be(expected); + [Theory, AutoNSubstituteData] + internal void Should_Return_Converted_Value( + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + FaultedEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); - [Theory, AutoNSubstituteData] - internal void Should_Return_FaultedEvent_When_Exception_IsThrown( - IEventMetadata metadata, - JsonSerializerOptions options, - KeyNotFoundException exception, - FaultedEventDataConverter sut) - => sut - .Convert( - metadata, - doc.RootElement, - options, - () => throw exception) - .Should() - .BeEquivalentTo( - new FaultedEvent( - doc.RootElement.GetRawText(), - exception)); - } + [Theory, AutoNSubstituteData] + internal void Should_Return_FaultedEvent_When_Exception_IsThrown( + IEventMetadata metadata, + JsonSerializerOptions options, + KeyNotFoundException exception, + FaultedEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => throw exception) + .Should() + .BeEquivalentTo( + new FaultedEvent( + doc.RootElement.GetRawText(), + exception)); } diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs index 4e5a1e5..913e4ff 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/NamedEventConverterTests.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; using Atc.Cosmos.EventStore.Converters; using Atc.Cosmos.EventStore.Events; @@ -9,53 +8,52 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Converters +namespace Atc.Cosmos.EventStore.Tests.Converters; + +public class NamedEventConverterTests { - public class NamedEventConverterTests - { - private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); - [Theory, AutoNSubstituteData] - internal void Should_Return_Value_FromNext_When_TypeName_IsNotFound( - [Frozen] IEventTypeProvider typeProvider, - IEventMetadata metadata, - JsonSerializerOptions options, - string expected, - NamedEventConverter sut) - { - typeProvider - .GetEventType(default) - .ReturnsForAnyArgs((Type)null); + [Theory, AutoNSubstituteData] + internal void Should_Return_Value_FromNext_When_TypeName_IsNotFound( + [Frozen] IEventTypeProvider typeProvider, + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + NamedEventConverter sut) + { + typeProvider + .GetEventType(default) + .ReturnsForAnyArgs((Type)null); - sut - .Convert( - metadata, - doc.RootElement, - options, - () => expected) - .Should() - .Be(expected); - } + sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); + } - [Theory, AutoNSubstituteData] - internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( - [Frozen] IEventTypeProvider typeProvider, - IEventMetadata metadata, - NamedEventConverter sut) - { - typeProvider - .GetEventType(metadata.Name) - .ReturnsForAnyArgs(typeof(EventOne)); + [Theory, AutoNSubstituteData] + internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( + [Frozen] IEventTypeProvider typeProvider, + IEventMetadata metadata, + NamedEventConverter sut) + { + typeProvider + .GetEventType(metadata.Name) + .ReturnsForAnyArgs(typeof(EventOne)); - sut - .Convert( - metadata, - doc.RootElement, - new JsonSerializerOptions(), - () => null) - .Should() - .BeEquivalentTo( - new EventOne("name", 42)); - } + sut + .Convert( + metadata, + doc.RootElement, + new JsonSerializerOptions(), + () => null) + .Should() + .BeEquivalentTo( + new EventOne("name", 42)); } } diff --git a/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs index 950012f..74529be 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Converters/UnknownEventDataConverterTests.cs @@ -5,40 +5,39 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Converters +namespace Atc.Cosmos.EventStore.Tests.Converters; + +public class UnknownEventDataConverterTests { - public class UnknownEventDataConverterTests - { - private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); + private readonly JsonDocument doc = JsonDocument.Parse(JsonSerializer.Serialize(new EventOne("name", 42))); - [Theory, AutoNSubstituteData] - internal void Should_Return_Converted_Value_Id_NotNull( - IEventMetadata metadata, - JsonSerializerOptions options, - string expected, - UnknownEventDataConverter sut) - => sut - .Convert( - metadata, - doc.RootElement, - options, - () => expected) - .Should() - .Be(expected); + [Theory, AutoNSubstituteData] + internal void Should_Return_Converted_Value_Id_NotNull( + IEventMetadata metadata, + JsonSerializerOptions options, + string expected, + UnknownEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => expected) + .Should() + .Be(expected); - [Theory, AutoNSubstituteData] - internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( - IEventMetadata metadata, - JsonSerializerOptions options, - UnknownEventDataConverter sut) - => sut - .Convert( - metadata, - doc.RootElement, - options, - () => null) - .Should() - .BeEquivalentTo( - new UnknownEvent(doc.RootElement.GetRawText())); - } + [Theory, AutoNSubstituteData] + internal void Should_Return_UnknownEvent_When_Value_IsNot_Converted( + IEventMetadata metadata, + JsonSerializerOptions options, + UnknownEventDataConverter sut) + => sut + .Convert( + metadata, + doc.RootElement, + options, + () => null) + .Should() + .BeEquivalentTo( + new UnknownEvent(doc.RootElement.GetRawText())); } diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosBatchWriterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosBatchWriterTests.cs index 844a641..31a6f02 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosBatchWriterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosBatchWriterTests.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; @@ -11,146 +8,145 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosBatchWriterTests { - public class CosmosBatchWriterTests + private readonly TransactionalBatchOperationResult operationResponse; + private readonly Container container; + private readonly TransactionalBatch transactionBatch; + private readonly StreamMetadata expectedMetadata; + private readonly TransactionalBatchResponse expectedTransactionResponse; + private readonly string expectedETag; + private readonly IEventStoreContainerProvider containerProvider; + private readonly IDateTimeProvider dateTimeProvider; + private readonly DateTimeOffset expectedDateTime; + private readonly CosmosBatchWriter sut; + + public CosmosBatchWriterTests() + { + expectedMetadata = new Fixture().Create(); + expectedETag = new Fixture().Create(); + operationResponse = Substitute.For>(); + operationResponse + .Resource + .Returns(expectedMetadata); + operationResponse + .ETag + .Returns(expectedETag); + + expectedTransactionResponse = Substitute.For(); + expectedTransactionResponse + .IsSuccessStatusCode + .Returns(returnThis: true); + expectedTransactionResponse + .GetOperationResultAtIndex(default) + .ReturnsForAnyArgs(operationResponse); + + transactionBatch = Substitute.For(); + transactionBatch + .UpsertItem(default, default) + .ReturnsForAnyArgs(transactionBatch); + transactionBatch + .CreateItem(default, default) + .ReturnsForAnyArgs(transactionBatch); + transactionBatch + .ExecuteAsync(default) + .ReturnsForAnyArgs(Task.FromResult(expectedTransactionResponse)); + + container = Substitute.For(); + container + .CreateTransactionalBatch(default) + .ReturnsForAnyArgs(transactionBatch); + + containerProvider = Substitute.For(); + containerProvider + .GetStreamContainer() + .Returns(container, returnThese: null); + containerProvider + .GetIndexContainer() + .Returns(container, returnThese: null); + + expectedDateTime = DateTimeOffset.UtcNow; + dateTimeProvider = Substitute.For(); + dateTimeProvider + .GetDateTime() + .Returns(expectedDateTime); + + sut = new CosmosBatchWriter(containerProvider, dateTimeProvider); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Create_TransactionBatch_With_StreamId_as_PartitionKey( + StreamBatch streamBatch, + CancellationToken cancellationToken) { - private readonly TransactionalBatchOperationResult operationResponse; - private readonly Container container; - private readonly TransactionalBatch transactionBatch; - private readonly StreamMetadata expectedMetadata; - private readonly TransactionalBatchResponse expectedTransactionResponse; - private readonly string expectedETag; - private readonly IEventStoreContainerProvider containerProvider; - private readonly IDateTimeProvider dateTimeProvider; - private readonly DateTimeOffset expectedDateTime; - private readonly CosmosBatchWriter sut; - - public CosmosBatchWriterTests() - { - expectedMetadata = new Fixture().Create(); - expectedETag = new Fixture().Create(); - operationResponse = Substitute.For>(); - operationResponse - .Resource - .Returns(expectedMetadata); - operationResponse - .ETag - .Returns(expectedETag); - - expectedTransactionResponse = Substitute.For(); - expectedTransactionResponse - .IsSuccessStatusCode - .Returns(returnThis: true); - expectedTransactionResponse - .GetOperationResultAtIndex(default) - .ReturnsForAnyArgs(operationResponse); - - transactionBatch = Substitute.For(); - transactionBatch - .UpsertItem(default, default) - .ReturnsForAnyArgs(transactionBatch); - transactionBatch - .CreateItem(default, default) - .ReturnsForAnyArgs(transactionBatch); - transactionBatch - .ExecuteAsync(default) - .ReturnsForAnyArgs(Task.FromResult(expectedTransactionResponse)); - - container = Substitute.For(); - container - .CreateTransactionalBatch(default) - .ReturnsForAnyArgs(transactionBatch); - - containerProvider = Substitute.For(); - containerProvider - .GetStreamContainer() - .Returns(container, returnThese: null); - containerProvider - .GetIndexContainer() - .Returns(container, returnThese: null); - - expectedDateTime = DateTimeOffset.UtcNow; - dateTimeProvider = Substitute.For(); - dateTimeProvider - .GetDateTime() - .Returns(expectedDateTime); - - sut = new CosmosBatchWriter(containerProvider, dateTimeProvider); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Create_TransactionBatch_With_StreamId_as_PartitionKey( - StreamBatch streamBatch, - CancellationToken cancellationToken) - { - await sut.WriteAsync(streamBatch, cancellationToken); - - _ = container - .Received() - .CreateTransactionalBatch( - new PartitionKey(streamBatch.Metadata.StreamId.Value)); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Upsert_Metadata_With_ETag( - StreamBatch streamBatch, - CancellationToken cancellationToken) - { - await sut.WriteAsync(streamBatch, cancellationToken); - - _ = transactionBatch - .Received() - .UpsertItem( - streamBatch.Metadata, - Arg.Is(opt => opt.IfMatchEtag == streamBatch.Metadata.ETag)); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Create_Documents( - StreamBatch streamBatch, - CancellationToken cancellationToken) - { - await sut.WriteAsync(streamBatch, cancellationToken); - - _ = transactionBatch - .Received(streamBatch.Documents.Count) - .CreateItem( - Arg.Any(), - Arg.Any()); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Throw_When_ExecutionFails( - StreamBatch streamBatch, - CancellationToken cancellationToken) - { - expectedTransactionResponse - .IsSuccessStatusCode - .Returns(returnThis: false); - - await FluentActions - .Awaiting(() => sut.WriteAsync(streamBatch, cancellationToken)) - .Should() - .ThrowAsync(); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Create_Index_When_Stream_IsEmpty( - StreamBatch streamBatch, - CancellationToken cancellationToken) - { - streamBatch.Metadata.ETag = null; - - await sut.WriteAsync(streamBatch, cancellationToken); - - _ = container - .Received() - .UpsertItemAsync( - Arg.Is(arg => arg.PartitionKey == nameof(StreamIndex) && arg.StreamId == streamBatch.Metadata.StreamId), - new PartitionKey(nameof(StreamIndex)), - Arg.Is(arg => arg.EnableContentResponseOnWrite == false), - cancellationToken); - } + await sut.WriteAsync(streamBatch, cancellationToken); + + _ = container + .Received() + .CreateTransactionalBatch( + new PartitionKey(streamBatch.Metadata.StreamId.Value)); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Upsert_Metadata_With_ETag( + StreamBatch streamBatch, + CancellationToken cancellationToken) + { + await sut.WriteAsync(streamBatch, cancellationToken); + + _ = transactionBatch + .Received() + .UpsertItem( + streamBatch.Metadata, + Arg.Is(opt => opt.IfMatchEtag == streamBatch.Metadata.ETag)); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Create_Documents( + StreamBatch streamBatch, + CancellationToken cancellationToken) + { + await sut.WriteAsync(streamBatch, cancellationToken); + + _ = transactionBatch + .Received(streamBatch.Documents.Count) + .CreateItem( + Arg.Any(), + Arg.Any()); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Throw_When_ExecutionFails( + StreamBatch streamBatch, + CancellationToken cancellationToken) + { + expectedTransactionResponse + .IsSuccessStatusCode + .Returns(returnThis: false); + + await FluentActions + .Awaiting(() => sut.WriteAsync(streamBatch, cancellationToken)) + .Should() + .ThrowAsync(); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Create_Index_When_Stream_IsEmpty( + StreamBatch streamBatch, + CancellationToken cancellationToken) + { + streamBatch.Metadata.ETag = null; + + await sut.WriteAsync(streamBatch, cancellationToken); + + _ = container + .Received() + .UpsertItemAsync( + Arg.Is(arg => arg.PartitionKey == nameof(StreamIndex) && arg.StreamId == streamBatch.Metadata.StreamId), + new PartitionKey(nameof(StreamIndex)), + Arg.Is(arg => arg.EnableContentResponseOnWrite == false), + cancellationToken); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs index 6e73356..6f826c2 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs @@ -1,6 +1,4 @@ using System.Net; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Test; using AutoFixture; @@ -10,143 +8,142 @@ using NSubstitute.ExceptionExtensions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosCheckpointReaderTests { - public class CosmosCheckpointReaderTests + private readonly ItemResponse> itemResponse; + private readonly Container container; + private readonly CheckpointDocument expectedCheckpoint; + private readonly IEventStoreContainerProvider containerProvider; + private readonly CosmosCheckpointReader sut; + + public CosmosCheckpointReaderTests() + { + expectedCheckpoint = new Fixture().Create>(); + itemResponse = Substitute.For>>(); + itemResponse + .Resource + .Returns(expectedCheckpoint); + + container = Substitute.For(); + container + .ReadItemAsync>(default, default, default, default) + .ReturnsForAnyArgs(itemResponse); + + containerProvider = Substitute.For(); + containerProvider + .GetIndexContainer() + .Returns(container, returnThese: null); + + sut = new CosmosCheckpointReader(containerProvider); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Read_From_Index_Container( + string name, + StreamId streamId, + CancellationToken cancellationToken) { - private readonly ItemResponse> itemResponse; - private readonly Container container; - private readonly CheckpointDocument expectedCheckpoint; - private readonly IEventStoreContainerProvider containerProvider; - private readonly CosmosCheckpointReader sut; - - public CosmosCheckpointReaderTests() - { - expectedCheckpoint = new Fixture().Create>(); - itemResponse = Substitute.For>>(); - itemResponse - .Resource - .Returns(expectedCheckpoint); - - container = Substitute.For(); - container - .ReadItemAsync>(default, default, default, default) - .ReturnsForAnyArgs(itemResponse); - - containerProvider = Substitute.For(); - containerProvider - .GetIndexContainer() - .Returns(container, returnThese: null); - - sut = new CosmosCheckpointReader(containerProvider); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Read_From_Index_Container( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - await sut - .ReadAsync(name, streamId, cancellationToken); - - containerProvider - .Received(1) - .GetIndexContainer(); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Use_Name_As_DocumentId( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - await sut - .ReadAsync(name, streamId, cancellationToken); - - _ = container - .Received(1) - .ReadItemAsync>( - name, - Arg.Any(), - Arg.Any(), - cancellationToken); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Use_StreamId_As_PartitionKey( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - await sut - .ReadAsync(name, streamId, cancellationToken); - - _ = container - .Received(1) - .ReadItemAsync>( - Arg.Any(), - Arg.Is(pk => pk == new PartitionKey(streamId.Value)), - Arg.Any(), - cancellationToken); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Return_Null_When_Document_IsNotFound( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync>(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var checkpoint = await sut.ReadAsync( + await sut + .ReadAsync(name, streamId, cancellationToken); + + containerProvider + .Received(1) + .GetIndexContainer(); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Use_Name_As_DocumentId( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + await sut + .ReadAsync(name, streamId, cancellationToken); + + _ = container + .Received(1) + .ReadItemAsync>( name, - streamId, + Arg.Any(), + Arg.Any(), cancellationToken); + } - checkpoint - .Should() - .BeNull(); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Propergate_CosmosException_When_StatusCode_IsNot_NotFound( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync>(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.TooManyRequests, 0, "a", 1)); - - await FluentActions - .Awaiting(() => sut.ReadAsync(name, streamId, cancellationToken)) - .Should() - .ThrowAsync(); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Return_Checkpoint( - string name, - StreamId streamId, - CancellationToken cancellationToken) - { - var checkpoint = await sut.ReadAsync( - name, - streamId, + [Theory, AutoNSubstituteData] + public async Task Should_Use_StreamId_As_PartitionKey( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + await sut + .ReadAsync(name, streamId, cancellationToken); + + _ = container + .Received(1) + .ReadItemAsync>( + Arg.Any(), + Arg.Is(pk => pk == new PartitionKey(streamId.Value)), + Arg.Any(), cancellationToken); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Return_Null_When_Document_IsNotFound( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync>(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var checkpoint = await sut.ReadAsync( + name, + streamId, + cancellationToken); + + checkpoint + .Should() + .BeNull(); + } - checkpoint - .Should() - .BeEquivalentTo( - new Checkpoint( - expectedCheckpoint.Name, - expectedCheckpoint.StreamId, - expectedCheckpoint.StreamVersion, - expectedCheckpoint.Timestamp, - expectedCheckpoint.State)); - } + [Theory, AutoNSubstituteData] + public async Task Should_Propergate_CosmosException_When_StatusCode_IsNot_NotFound( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync>(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.TooManyRequests, 0, "a", 1)); + + await FluentActions + .Awaiting(() => sut.ReadAsync(name, streamId, cancellationToken)) + .Should() + .ThrowAsync(); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Return_Checkpoint( + string name, + StreamId streamId, + CancellationToken cancellationToken) + { + var checkpoint = await sut.ReadAsync( + name, + streamId, + cancellationToken); + + checkpoint + .Should() + .BeEquivalentTo( + new Checkpoint( + expectedCheckpoint.Name, + expectedCheckpoint.StreamId, + expectedCheckpoint.StreamVersion, + expectedCheckpoint.Timestamp, + expectedCheckpoint.State)); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointWriterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointWriterTests.cs index f8301b3..39d3005 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointWriterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointWriterTests.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Test; @@ -9,124 +6,123 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosCheckpointWriterTests { - public class CosmosCheckpointWriterTests + private readonly ItemResponse> itemResponse; + private readonly Container container; + private readonly CheckpointDocument expectedCheckpoint; + private readonly DateTimeOffset expectedTimestamp; + private readonly IDateTimeProvider dateTimeProvider; + private readonly IEventStoreContainerProvider containerProvider; + private readonly CosmosCheckpointWriter sut; + + public CosmosCheckpointWriterTests() { - private readonly ItemResponse> itemResponse; - private readonly Container container; - private readonly CheckpointDocument expectedCheckpoint; - private readonly DateTimeOffset expectedTimestamp; - private readonly IDateTimeProvider dateTimeProvider; - private readonly IEventStoreContainerProvider containerProvider; - private readonly CosmosCheckpointWriter sut; + expectedCheckpoint = new Fixture().Create>(); + itemResponse = Substitute.For>>(); + itemResponse + .Resource + .Returns(expectedCheckpoint); - public CosmosCheckpointWriterTests() - { - expectedCheckpoint = new Fixture().Create>(); - itemResponse = Substitute.For>>(); - itemResponse - .Resource - .Returns(expectedCheckpoint); + container = Substitute.For(); + container + .UpsertItemAsync>(default, default, default, default) + .ReturnsForAnyArgs(itemResponse); - container = Substitute.For(); - container - .UpsertItemAsync>(default, default, default, default) - .ReturnsForAnyArgs(itemResponse); + containerProvider = Substitute.For(); + containerProvider + .GetIndexContainer() + .Returns(container, returnThese: null); - containerProvider = Substitute.For(); - containerProvider - .GetIndexContainer() - .Returns(container, returnThese: null); + expectedTimestamp = DateTimeOffset.UtcNow; + dateTimeProvider = Substitute.For(); + dateTimeProvider + .GetDateTime() + .Returns(expectedTimestamp); - expectedTimestamp = DateTimeOffset.UtcNow; - dateTimeProvider = Substitute.For(); - dateTimeProvider - .GetDateTime() - .Returns(expectedTimestamp); + sut = new CosmosCheckpointWriter( + containerProvider, + dateTimeProvider); + } - sut = new CosmosCheckpointWriter( - containerProvider, - dateTimeProvider); - } + [Theory, AutoNSubstituteData] + public async Task Should_Use_StreamId_As_PartitionKey( + string name, + StreamId streamId, + StreamVersion streamVersion, + string state, + CancellationToken cancellationToken) + { + await sut.WriteAsync( + name, + streamId, + streamVersion, + state, + cancellationToken); - [Theory, AutoNSubstituteData] - public async Task Should_Use_StreamId_As_PartitionKey( - string name, - StreamId streamId, - StreamVersion streamVersion, - string state, - CancellationToken cancellationToken) - { - await sut.WriteAsync( - name, - streamId, - streamVersion, - state, + _ = container + .Received() + .UpsertItemAsync( + Arg.Any(), + new PartitionKey(streamId.Value), + Arg.Any(), cancellationToken); + } - _ = container - .Received() - .UpsertItemAsync( - Arg.Any(), - new PartitionKey(streamId.Value), - Arg.Any(), - cancellationToken); - } + [Theory, AutoNSubstituteData] + public async Task Should_Write_Checkpoint( + string name, + StreamId streamId, + StreamVersion streamVersion, + string state, + CancellationToken cancellationToken) + { + var expectedDocument = new CheckpointDocument( + name, + streamId, + streamVersion, + dateTimeProvider.GetDateTime(), + state); - [Theory, AutoNSubstituteData] - public async Task Should_Write_Checkpoint( - string name, - StreamId streamId, - StreamVersion streamVersion, - string state, - CancellationToken cancellationToken) - { - var expectedDocument = new CheckpointDocument( - name, - streamId, - streamVersion, - dateTimeProvider.GetDateTime(), - state); + await sut.WriteAsync( + name, + streamId, + streamVersion, + state, + cancellationToken); - await sut.WriteAsync( - name, - streamId, - streamVersion, - state, + _ = container + .Received() + .UpsertItemAsync( + Arg.Is(doc => doc == expectedDocument), + Arg.Any(), + Arg.Any(), cancellationToken); + } - _ = container - .Received() - .UpsertItemAsync( - Arg.Is(doc => doc == expectedDocument), - Arg.Any(), - Arg.Any(), - cancellationToken); - } + [Theory, AutoNSubstituteData] + public async Task Should_Have_ContentResponse_Disabled( + string name, + StreamId streamId, + StreamVersion streamVersion, + string state, + CancellationToken cancellationToken) + { + await sut.WriteAsync( + name, + streamId, + streamVersion, + state, + cancellationToken); - [Theory, AutoNSubstituteData] - public async Task Should_Have_ContentResponse_Disabled( - string name, - StreamId streamId, - StreamVersion streamVersion, - string state, - CancellationToken cancellationToken) - { - await sut.WriteAsync( - name, - streamId, - streamVersion, - state, + _ = container + .Received() + .UpsertItemAsync( + Arg.Any(), + Arg.Any(), + Arg.Is(options => options.EnableContentResponseOnWrite == false), cancellationToken); - - _ = container - .Received() - .UpsertItemAsync( - Arg.Any(), - Arg.Any(), - Arg.Is(options => options.EnableContentResponseOnWrite == false), - cancellationToken); - } } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosContainerProviderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosContainerProviderTests.cs index 87ffe8d..b2361c5 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosContainerProviderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosContainerProviderTests.cs @@ -8,98 +8,97 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosContainerProviderTests { - public class CosmosContainerProviderTests + [Theory, AutoNSubstituteData] + internal void Should_Provide_StreamContainer( + [Frozen] ICosmosClientFactory cosmosFactory, + [Substitute] CosmosClient cosmosClient, + [Substitute] IOptions options, + [Substitute] Container container) { - [Theory, AutoNSubstituteData] - internal void Should_Provide_StreamContainer( - [Frozen] ICosmosClientFactory cosmosFactory, - [Substitute] CosmosClient cosmosClient, - [Substitute] IOptions options, - [Substitute] Container container) - { - options - .Value - .Returns(new EventStoreClientOptions()); - cosmosClient - .GetContainer(default, default) - .ReturnsForAnyArgs(container); - cosmosFactory - .GetClient() - .Returns(cosmosClient); + options + .Value + .Returns(new EventStoreClientOptions()); + cosmosClient + .GetContainer(default, default) + .ReturnsForAnyArgs(container); + cosmosFactory + .GetClient() + .Returns(cosmosClient); - var sut = new CosmosContainerProvider(cosmosFactory, options); + var sut = new CosmosContainerProvider(cosmosFactory, options); - sut.GetStreamContainer() - .Should() - .Be(container); + sut.GetStreamContainer() + .Should() + .Be(container); - cosmosClient - .Received(1) - .GetContainer( - options.Value.EventStoreDatabaseId, - options.Value.EventStoreContainerId); - } + cosmosClient + .Received(1) + .GetContainer( + options.Value.EventStoreDatabaseId, + options.Value.EventStoreContainerId); + } - [Theory, AutoNSubstituteData] - internal void Should_Provide_SubscriptionContainer( - [Frozen] ICosmosClientFactory cosmosFactory, - [Substitute] CosmosClient cosmosClient, - [Substitute] IOptions options, - [Substitute] Container container) - { - options - .Value - .Returns(new EventStoreClientOptions()); - cosmosClient - .GetContainer(default, default) - .ReturnsForAnyArgs(container); - cosmosFactory - .GetClient() - .Returns(cosmosClient); + [Theory, AutoNSubstituteData] + internal void Should_Provide_SubscriptionContainer( + [Frozen] ICosmosClientFactory cosmosFactory, + [Substitute] CosmosClient cosmosClient, + [Substitute] IOptions options, + [Substitute] Container container) + { + options + .Value + .Returns(new EventStoreClientOptions()); + cosmosClient + .GetContainer(default, default) + .ReturnsForAnyArgs(container); + cosmosFactory + .GetClient() + .Returns(cosmosClient); - var sut = new CosmosContainerProvider(cosmosFactory, options); + var sut = new CosmosContainerProvider(cosmosFactory, options); - sut.GetSubscriptionContainer() - .Should() - .Be(container); + sut.GetSubscriptionContainer() + .Should() + .Be(container); - cosmosClient - .Received(1) - .GetContainer( - options.Value.EventStoreDatabaseId, - options.Value.SubscriptionContainerId); - } + cosmosClient + .Received(1) + .GetContainer( + options.Value.EventStoreDatabaseId, + options.Value.SubscriptionContainerId); + } - [Theory, AutoNSubstituteData] - internal void Should_Provide_IndexContainer( - [Frozen] ICosmosClientFactory cosmosFactory, - [Substitute] CosmosClient cosmosClient, - [Substitute] IOptions options, - [Substitute] Container container) - { - options - .Value - .Returns(new EventStoreClientOptions()); - cosmosClient - .GetContainer(default, default) - .ReturnsForAnyArgs(container); - cosmosFactory - .GetClient() - .Returns(cosmosClient); + [Theory, AutoNSubstituteData] + internal void Should_Provide_IndexContainer( + [Frozen] ICosmosClientFactory cosmosFactory, + [Substitute] CosmosClient cosmosClient, + [Substitute] IOptions options, + [Substitute] Container container) + { + options + .Value + .Returns(new EventStoreClientOptions()); + cosmosClient + .GetContainer(default, default) + .ReturnsForAnyArgs(container); + cosmosFactory + .GetClient() + .Returns(cosmosClient); - var sut = new CosmosContainerProvider(cosmosFactory, options); + var sut = new CosmosContainerProvider(cosmosFactory, options); - sut.GetIndexContainer() - .Should() - .Be(container); + sut.GetIndexContainer() + .Should() + .Be(container); - cosmosClient - .Received(1) - .GetContainer( - options.Value.EventStoreDatabaseId, - options.Value.IndexContainerId); - } + cosmosClient + .Received(1) + .GetContainer( + options.Value.EventStoreDatabaseId, + options.Value.IndexContainerId); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs index 987b406..c0ee20e 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs @@ -1,7 +1,4 @@ -using System; using System.Net; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; @@ -13,210 +10,209 @@ using NSubstitute.ExceptionExtensions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosMetadataReaderTests { - public class CosmosMetadataReaderTests + private readonly ItemResponse itemResponse; + private readonly Container container; + private readonly StreamMetadata expectedMetadata; + private readonly DateTimeOffset expectedTimestamp; + private readonly string expectedETag; + private readonly IEventStoreContainerProvider containerProvider; + private readonly IDateTimeProvider dateTimeProvider; + private readonly CosmosMetadataReader sut; + + public CosmosMetadataReaderTests() + { + expectedMetadata = new Fixture().Create(); + expectedETag = new Fixture().Create(); + itemResponse = Substitute.For>(); + itemResponse + .Resource + .Returns(expectedMetadata); + itemResponse + .ETag + .Returns(expectedETag); + + container = Substitute.For(); + container + .ReadItemAsync(default, default, default, default) + .ReturnsForAnyArgs(itemResponse); + + expectedTimestamp = DateTimeOffset.UtcNow; + dateTimeProvider = Substitute.For(); + dateTimeProvider + .GetDateTime() + .Returns(expectedTimestamp); + + containerProvider = Substitute.For(); + containerProvider + .GetStreamContainer() + .Returns(container, returnThese: null); + sut = new CosmosMetadataReader(containerProvider, dateTimeProvider); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Use_StreamId_As_PartitionKey_When_ReadItem( + StreamId streamId, + CancellationToken cancellationToken) + { + await sut.GetAsync(streamId, cancellationToken); + + _ = container + .Received() + .ReadItemAsync( + Arg.Any(), + new PartitionKey(streamId.Value), + Arg.Any(), + cancellationToken); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Use_Fixed_Value_As_Id_When_ReadItem( + StreamId streamId, + CancellationToken cancellationToken) + { + await sut.GetAsync(streamId, cancellationToken); + + _ = container + .Received() + .ReadItemAsync( + StreamMetadata.StreamMetadataId, + Arg.Any(), + Arg.Any(), + cancellationToken); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_ETag_From_Response( + StreamId streamId, + CancellationToken cancellationToken) + { + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + metadata + .ETag + .Should() + .Be(expectedETag); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Return_Response( + StreamId streamId, + CancellationToken cancellationToken) + { + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + metadata + .Should() + .BeEquivalentTo(expectedMetadata); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_State_New_When_Document_IsNotFound( + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + metadata + .State + .Should() + .Be(StreamState.New); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_StreamVersion_StartOfStream_When_Document_IsNotFound( + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + metadata + .Version + .Should() + .Be(StreamVersion.StartOfStream); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_Correct_Id_When_Document_IsNotFound( + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + ((StreamMetadata)metadata) + .Id + .Should() + .Be(StreamMetadata.StreamMetadataId); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_StreamId_As_PartitionKey_When_Document_IsNotFound( + StreamId streamId, + CancellationToken cancellationToken) + { + container + .ReadItemAsync(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + ((StreamMetadata)metadata) + .PartitionKey + .Should() + .Be(streamId.Value); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Have_Timestamp_When_Document_IsNotFound( + StreamId streamId, + CancellationToken cancellationToken) { - private readonly ItemResponse itemResponse; - private readonly Container container; - private readonly StreamMetadata expectedMetadata; - private readonly DateTimeOffset expectedTimestamp; - private readonly string expectedETag; - private readonly IEventStoreContainerProvider containerProvider; - private readonly IDateTimeProvider dateTimeProvider; - private readonly CosmosMetadataReader sut; - - public CosmosMetadataReaderTests() - { - expectedMetadata = new Fixture().Create(); - expectedETag = new Fixture().Create(); - itemResponse = Substitute.For>(); - itemResponse - .Resource - .Returns(expectedMetadata); - itemResponse - .ETag - .Returns(expectedETag); - - container = Substitute.For(); - container - .ReadItemAsync(default, default, default, default) - .ReturnsForAnyArgs(itemResponse); - - expectedTimestamp = DateTimeOffset.UtcNow; - dateTimeProvider = Substitute.For(); - dateTimeProvider - .GetDateTime() - .Returns(expectedTimestamp); - - containerProvider = Substitute.For(); - containerProvider - .GetStreamContainer() - .Returns(container, returnThese: null); - sut = new CosmosMetadataReader(containerProvider, dateTimeProvider); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Use_StreamId_As_PartitionKey_When_ReadItem( - StreamId streamId, - CancellationToken cancellationToken) - { - await sut.GetAsync(streamId, cancellationToken); - - _ = container - .Received() - .ReadItemAsync( - Arg.Any(), - new PartitionKey(streamId.Value), - Arg.Any(), - cancellationToken); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Use_Fixed_Value_As_Id_When_ReadItem( - StreamId streamId, - CancellationToken cancellationToken) - { - await sut.GetAsync(streamId, cancellationToken); - - _ = container - .Received() - .ReadItemAsync( - StreamMetadata.StreamMetadataId, - Arg.Any(), - Arg.Any(), - cancellationToken); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_ETag_From_Response( - StreamId streamId, - CancellationToken cancellationToken) - { - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - metadata - .ETag - .Should() - .Be(expectedETag); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Return_Response( - StreamId streamId, - CancellationToken cancellationToken) - { - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - metadata - .Should() - .BeEquivalentTo(expectedMetadata); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_State_New_When_Document_IsNotFound( - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - metadata - .State - .Should() - .Be(StreamState.New); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_StreamVersion_StartOfStream_When_Document_IsNotFound( - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - metadata - .Version - .Should() - .Be(StreamVersion.StartOfStream); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_Correct_Id_When_Document_IsNotFound( - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - ((StreamMetadata)metadata) - .Id - .Should() - .Be(StreamMetadata.StreamMetadataId); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_StreamId_As_PartitionKey_When_Document_IsNotFound( - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - ((StreamMetadata)metadata) - .PartitionKey - .Should() - .Be(streamId.Value); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Have_Timestamp_When_Document_IsNotFound( - StreamId streamId, - CancellationToken cancellationToken) - { - container - .ReadItemAsync(default, default, default, default) - .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); - - var metadata = await sut - .GetAsync( - streamId, - cancellationToken); - - metadata - .Timestamp - .Should() - .Be(expectedTimestamp); - } + container + .ReadItemAsync(default, default, default, default) + .ThrowsForAnyArgs(new CosmosException("error", HttpStatusCode.NotFound, 0, "a", 1)); + + var metadata = await sut + .GetAsync( + streamId, + cancellationToken); + + metadata + .Timestamp + .Should() + .Be(expectedTimestamp); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs index 9a25ead..e63fbe8 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Cosmos; using Atc.Cosmos.EventStore.Events; using Atc.Test; @@ -9,227 +6,226 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Cosmos +namespace Atc.Cosmos.EventStore.Tests.Cosmos; + +public class CosmosStreamIteratorTests { - public class CosmosStreamIteratorTests - { - private readonly FeedResponse feedResponse; - private readonly FeedIterator feedIterator; - private readonly Container container; - private readonly IEventStoreContainerProvider containerProvider; + private readonly FeedResponse feedResponse; + private readonly FeedIterator feedIterator; + private readonly Container container; + private readonly IEventStoreContainerProvider containerProvider; - private readonly IEnumerable> expectedEvents; - private readonly CosmosStreamIterator sut; + private readonly IEnumerable> expectedEvents; + private readonly CosmosStreamIterator sut; - private QueryDefinition query = new("SELECT * FROM c"); - private QueryRequestOptions options = new(); + private QueryDefinition query = new("SELECT * FROM c"); + private QueryRequestOptions options = new(); - public class TestEvent - { - public string Name { get; set; } = string.Empty; - } + public class TestEvent + { + public string Name { get; set; } = string.Empty; + } - public CosmosStreamIteratorTests() + public CosmosStreamIteratorTests() + { + expectedEvents = new List> { - expectedEvents = new List> - { - new EventDocument( - new TestEvent { Name = "test1" }, - new EventMetadata()), - new EventDocument( - new TestEvent { Name = "test2" }, - new EventMetadata()), - new EventDocument( - new TestEvent { Name = "test3" }, - new EventMetadata()), - }; - feedResponse = Substitute.For>(); - feedResponse - .Resource - .Returns(expectedEvents); - - feedIterator = Substitute.For>(); - feedIterator - .HasMoreResults - .Returns(returnThis: true, returnThese: false); - feedIterator - .ReadNextAsync(default) - .Returns(feedResponse); - - container = Substitute.For(); - container - .GetItemQueryIterator( - Arg.Do(q => query = q), - Arg.Any(), - Arg.Do(o => options = o)) - .ReturnsForAnyArgs(feedIterator); - - containerProvider = Substitute.For(); - containerProvider - .GetStreamContainer() - .Returns(container, returnThese: null); - - sut = new CosmosStreamIterator(containerProvider); - } + new EventDocument( + new TestEvent { Name = "test1" }, + new EventMetadata()), + new EventDocument( + new TestEvent { Name = "test2" }, + new EventMetadata()), + new EventDocument( + new TestEvent { Name = "test3" }, + new EventMetadata()), + }; + feedResponse = Substitute.For>(); + feedResponse + .Resource + .Returns(expectedEvents); + + feedIterator = Substitute.For>(); + feedIterator + .HasMoreResults + .Returns(returnThis: true, returnThese: false); + feedIterator + .ReadNextAsync(default) + .Returns(feedResponse); + + container = Substitute.For(); + container + .GetItemQueryIterator( + Arg.Do(q => query = q), + Arg.Any(), + Arg.Do(o => options = o)) + .ReturnsForAnyArgs(feedIterator); + + containerProvider = Substitute.For(); + containerProvider + .GetStreamContainer() + .Returns(container, returnThese: null); + + sut = new CosmosStreamIterator(containerProvider); + } - [Theory, AutoNSubstituteData] - public async Task Should_Iterate_All_Items( - StreamId streamId, - CancellationToken cancellationToken) - { - var received = await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); + [Theory, AutoNSubstituteData] + public async Task Should_Iterate_All_Items( + StreamId streamId, + CancellationToken cancellationToken) + { + var received = await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); - received - .Should() - .BeEquivalentTo(expectedEvents); - } + received + .Should() + .BeEquivalentTo(expectedEvents); + } - [Theory, AutoNSubstituteData] - public async Task Should_Use_PartitionKey( - StreamId streamId, - CancellationToken cancellationToken) - { - await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); + [Theory, AutoNSubstituteData] + public async Task Should_Use_PartitionKey( + StreamId streamId, + CancellationToken cancellationToken) + { + await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); - options - .PartitionKey - .Should() - .Be(new PartitionKey(streamId.Value)); - } + options + .PartitionKey + .Should() + .Be(new PartitionKey(streamId.Value)); + } - [Theory, AutoNSubstituteData] - public async Task Should_Query_All_When_StreamVersion_Is_Any( - StreamId streamId, - CancellationToken cancellationToken) - { - await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); - - query - .QueryText - .Should() - .Be("SELECT * FROM e WHERE e.pk = @partitionKey ORDER BY e.properties.version"); - query - .GetQueryParameters() - .Should() - .HaveCount(1); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@partitionKey", streamId.Value)); - } + [Theory, AutoNSubstituteData] + public async Task Should_Query_All_When_StreamVersion_Is_Any( + StreamId streamId, + CancellationToken cancellationToken) + { + await ReadStream(streamId, StreamVersion.Any, null, cancellationToken); + + query + .QueryText + .Should() + .Be("SELECT * FROM e WHERE e.pk = @partitionKey ORDER BY e.properties.version"); + query + .GetQueryParameters() + .Should() + .HaveCount(1); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@partitionKey", streamId.Value)); + } - [Theory, AutoNSubstituteData] - public async Task Should_Query_From_StreamVersion( - StreamId streamId, - CancellationToken cancellationToken) - { - await ReadStream(streamId, 10, null, cancellationToken); - - query - .QueryText - .Should() - .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion ORDER BY e.properties.version"); - query - .GetQueryParameters() - .Should() - .HaveCount(2); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@partitionKey", streamId.Value)); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@fromVersion", 10L)); - } + [Theory, AutoNSubstituteData] + public async Task Should_Query_From_StreamVersion( + StreamId streamId, + CancellationToken cancellationToken) + { + await ReadStream(streamId, 10, null, cancellationToken); + + query + .QueryText + .Should() + .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion ORDER BY e.properties.version"); + query + .GetQueryParameters() + .Should() + .HaveCount(2); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@partitionKey", streamId.Value)); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@fromVersion", 10L)); + } - [Theory, AutoNSubstituteData] - public async Task Should_Include_EventName( - StreamId streamId, - CancellationToken cancellationToken) - { - await ReadStream( - streamId, - 10, - new StreamReadFilter { IncludeEvents = new EventName[] { "evt1" } }, - cancellationToken); - - query - .QueryText - .Should() - .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion AND (e.properties.name = @name1) ORDER BY e.properties.version"); - query - .GetQueryParameters() - .Should() - .HaveCount(3); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@partitionKey", streamId.Value)); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@fromVersion", 10L)); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@name1", (EventName)"evt1")); - } + [Theory, AutoNSubstituteData] + public async Task Should_Include_EventName( + StreamId streamId, + CancellationToken cancellationToken) + { + await ReadStream( + streamId, + 10, + new StreamReadFilter { IncludeEvents = new EventName[] { "evt1" } }, + cancellationToken); + + query + .QueryText + .Should() + .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion AND (e.properties.name = @name1) ORDER BY e.properties.version"); + query + .GetQueryParameters() + .Should() + .HaveCount(3); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@partitionKey", streamId.Value)); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@fromVersion", 10L)); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@name1", (EventName)"evt1")); + } - [Theory, AutoNSubstituteData] - public async Task Should_Include_Multiple_EventNames( - StreamId streamId, - CancellationToken cancellationToken) - { - await ReadStream( - streamId, - 10, - new StreamReadFilter { IncludeEvents = new EventName[] { "evt1", "evt2", "evt3" } }, - cancellationToken); - - query - .QueryText - .Should() - .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion AND (e.properties.name = @name1 OR e.properties.name = @name2 OR e.properties.name = @name3) ORDER BY e.properties.version"); - query - .GetQueryParameters() - .Should() - .HaveCount(5); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@partitionKey", streamId.Value)); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@fromVersion", 10L)); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@name1", (EventName)"evt1")); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@name2", (EventName)"evt2")); - query - .GetQueryParameters() - .Should() - .Contain((Name: "@name3", (EventName)"evt3")); - } + [Theory, AutoNSubstituteData] + public async Task Should_Include_Multiple_EventNames( + StreamId streamId, + CancellationToken cancellationToken) + { + await ReadStream( + streamId, + 10, + new StreamReadFilter { IncludeEvents = new EventName[] { "evt1", "evt2", "evt3" } }, + cancellationToken); + + query + .QueryText + .Should() + .Be("SELECT * FROM e WHERE e.pk = @partitionKey AND e.properties.version >= @fromVersion AND (e.properties.name = @name1 OR e.properties.name = @name2 OR e.properties.name = @name3) ORDER BY e.properties.version"); + query + .GetQueryParameters() + .Should() + .HaveCount(5); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@partitionKey", streamId.Value)); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@fromVersion", 10L)); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@name1", (EventName)"evt1")); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@name2", (EventName)"evt2")); + query + .GetQueryParameters() + .Should() + .Contain((Name: "@name3", (EventName)"evt3")); + } - private async Task> ReadStream( - StreamId streamId, - StreamVersion streamVersion, - StreamReadFilter filter, - CancellationToken cancellationToken) + private async Task> ReadStream( + StreamId streamId, + StreamVersion streamVersion, + StreamReadFilter filter, + CancellationToken cancellationToken) + { + var received = new List(); + await foreach (var item in sut.ReadAsync(streamId, streamVersion, filter, cancellationToken).ConfigureAwait(false)) { - var received = new List(); - await foreach (var item in sut.ReadAsync(streamId, streamVersion, filter, cancellationToken).ConfigureAwait(false)) - { - received.Add(item); - } - - return received; + received.Add(item); } + + return received; } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs index 4ed1cc2..3edb22b 100644 --- a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs @@ -1,73 +1,67 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Atc.Test; using Azure.Core; using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests +namespace Atc.Cosmos.EventStore.Tests; + +public class EventStoreClientOptionsTests { - public class EventStoreClientOptionsTests + [Fact] + internal void Should_Default_ToCosmosEmulator() { - [Fact] - internal void Should_Default_ToCosmosEmulator() - { #pragma warning disable CS0618 // Type or member is obsolete - var options = new EventStoreClientOptions(); + var options = new EventStoreClientOptions(); - options.ConnectionString.Should().Be(EventStoreClientOptions.CosmosEmulatorConnectionString); + options.ConnectionString.Should().Be(EventStoreClientOptions.CosmosEmulatorConnectionString); #pragma warning restore CS0618 // Type or member is obsolete - options.AuthKey.Should().BeNull(); - options.Endpoint.Should().BeNull(); - options.Credential.Should().BeNull(); - } + options.AuthKey.Should().BeNull(); + options.Endpoint.Should().BeNull(); + options.Credential.Should().BeNull(); + } - [Fact] - internal void Should_Set_ConnectionString() - { + [Fact] + internal void Should_Set_ConnectionString() + { #pragma warning disable CS0618 // Type or member is obsolete - var options = new EventStoreClientOptions - { - ConnectionString = "connection-string", - }; + var options = new EventStoreClientOptions + { + ConnectionString = "connection-string", + }; - options.ConnectionString.Should().Be("connection-string"); + options.ConnectionString.Should().Be("connection-string"); #pragma warning restore CS0618 // Type or member is obsolete - options.AuthKey.Should().BeNull(); - options.Endpoint.Should().BeNull(); - options.Credential.Should().BeNull(); - } + options.AuthKey.Should().BeNull(); + options.Endpoint.Should().BeNull(); + options.Credential.Should().BeNull(); + } - [Fact] - internal void Should_UseAuthKeyAndEndpoint() - { - var options = new EventStoreClientOptions(); - options.UseCredentials("endpoint", "auth-key"); + [Fact] + internal void Should_UseAuthKeyAndEndpoint() + { + var options = new EventStoreClientOptions(); + options.UseCredentials("endpoint", "auth-key"); - options.AuthKey.Should().Be("auth-key"); - options.Endpoint.Should().Be("endpoint"); - options.Credential.Should().BeNull(); + options.AuthKey.Should().Be("auth-key"); + options.Endpoint.Should().Be("endpoint"); + options.Credential.Should().BeNull(); #pragma warning disable CS0618 // Type or member is obsolete - options.ConnectionString.Should().BeNull(); + options.ConnectionString.Should().BeNull(); #pragma warning restore CS0618 // Type or member is obsolete - } + } - [Theory, AutoNSubstituteData] - internal void Should_UseCredentialToken( - TokenCredential token) - { - var options = new EventStoreClientOptions(); - options.UseCredentials("endpoint", token); + [Theory, AutoNSubstituteData] + internal void Should_UseCredentialToken( + TokenCredential token) + { + var options = new EventStoreClientOptions(); + options.UseCredentials("endpoint", token); - options.Endpoint.Should().Be("endpoint"); - options.Credential.Should().Be(token); - options.AuthKey.Should().BeNull(); + options.Endpoint.Should().Be("endpoint"); + options.Credential.Should().Be(token); + options.AuthKey.Should().BeNull(); #pragma warning disable CS0618 // Type or member is obsolete - options.ConnectionString.Should().BeNull(); + options.ConnectionString.Should().BeNull(); #pragma warning restore CS0618 // Type or member is obsolete - } } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientTests.cs b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientTests.cs index 2a9446f..43cabbb 100644 --- a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientTests.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using AutoFixture.AutoNSubstitute; @@ -11,210 +7,209 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests +namespace Atc.Cosmos.EventStore.Tests; + +public class EventStoreClientTests { - public class EventStoreClientTests + [Theory, AutoNSubstituteData] + internal async Task Should_DeleteSubscription( + [Frozen] IStreamSubscriptionRemover remover, + EventStoreClient sut, + ConsumerGroup consumerGroup, + CancellationToken cancellationToken) { - [Theory, AutoNSubstituteData] - internal async Task Should_DeleteSubscription( - [Frozen] IStreamSubscriptionRemover remover, - EventStoreClient sut, - ConsumerGroup consumerGroup, - CancellationToken cancellationToken) - { - await sut.DeleteSubscriptionAsync( - consumerGroup, - cancellationToken: cancellationToken); - - _ = remover - .Received(1) - .DeleteAsync( - consumerGroup, - cancellationToken); - } - - [Theory, AutoNSubstituteData] - internal Task Should_Throw_On_DeleteSubscription_When_ConsumerGroup_IsNull( - EventStoreClient sut, - CancellationToken cancellationToken) - => FluentActions - .Awaiting(() => sut.DeleteSubscriptionAsync(null, cancellationToken)) - .Should() - .ThrowAsync(); - - [Theory, AutoNSubstituteData] - internal async Task Should_Return_Info_On_GetStreamInfo( - [Frozen] IStreamInfoReader reader, - EventStoreClient sut, - StreamId streamId, - IStreamMetadata expectedResult, - CancellationToken cancellationToken) - { - reader - .ReadAsync(default, default) - .ReturnsForAnyArgs(expectedResult); - - var info = await sut.GetStreamInfoAsync( - streamId, - cancellationToken: cancellationToken); - - info - .Should() - .Be(expectedResult); - } - - [Theory, AutoNSubstituteData] - internal async ValueTask Should_WriteToStream( - [Frozen, Substitute] IStreamWriter writer, - EventStoreClient sut, - StreamId streamId, - IReadOnlyList events, - StreamResponse expected, - CancellationToken cancellationToken) - { - writer - .WriteAsync(default, default, default, default, default) - .ReturnsForAnyArgs(Task.FromResult(expected)); - - var result = await sut.WriteToStreamAsync( + await sut.DeleteSubscriptionAsync( + consumerGroup, + cancellationToken: cancellationToken); + + _ = remover + .Received(1) + .DeleteAsync( + consumerGroup, + cancellationToken); + } + + [Theory, AutoNSubstituteData] + internal Task Should_Throw_On_DeleteSubscription_When_ConsumerGroup_IsNull( + EventStoreClient sut, + CancellationToken cancellationToken) + => FluentActions + .Awaiting(() => sut.DeleteSubscriptionAsync(null, cancellationToken)) + .Should() + .ThrowAsync(); + + [Theory, AutoNSubstituteData] + internal async Task Should_Return_Info_On_GetStreamInfo( + [Frozen] IStreamInfoReader reader, + EventStoreClient sut, + StreamId streamId, + IStreamMetadata expectedResult, + CancellationToken cancellationToken) + { + reader + .ReadAsync(default, default) + .ReturnsForAnyArgs(expectedResult); + + var info = await sut.GetStreamInfoAsync( + streamId, + cancellationToken: cancellationToken); + + info + .Should() + .Be(expectedResult); + } + + [Theory, AutoNSubstituteData] + internal async ValueTask Should_WriteToStream( + [Frozen, Substitute] IStreamWriter writer, + EventStoreClient sut, + StreamId streamId, + IReadOnlyList events, + StreamResponse expected, + CancellationToken cancellationToken) + { + writer + .WriteAsync(default, default, default, default, default) + .ReturnsForAnyArgs(Task.FromResult(expected)); + + var result = await sut.WriteToStreamAsync( + streamId, + events, + StreamVersion.StartOfStream, + cancellationToken: cancellationToken); + + result + .Should() + .BeEquivalentTo(expected); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_Throw_When_EventsList_Containes_NullObject( + EventStoreClient sut, + StreamId streamId, + Collection events, + CancellationToken cancellationToken) + { + events.Add(null); + + await FluentActions + .Awaiting(() => sut.WriteToStreamAsync( streamId, events, StreamVersion.StartOfStream, - cancellationToken: cancellationToken); - - result - .Should() - .BeEquivalentTo(expected); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_Throw_When_EventsList_Containes_NullObject( - EventStoreClient sut, - StreamId streamId, - Collection events, - CancellationToken cancellationToken) - { - events.Add(null); - - await FluentActions - .Awaiting(() => sut.WriteToStreamAsync( + cancellationToken: cancellationToken)) + .Should() + .ThrowAsync(); + } + + [Theory, AutoNSubstituteData] + internal async Task Should_SetStreamCheckpoint( + [Frozen] IStreamCheckpointWriter writer, + EventStoreClient sut, + string name, + StreamId streamId, + StreamVersion streamVersion, + object state, + CancellationToken cancellationToken) + { + await sut.SetStreamCheckpointAsync( + name, + streamId, + streamVersion, + state, + cancellationToken); + + _ = writer + .Received(1) + .WriteAsync( + name, streamId, - events, - StreamVersion.StartOfStream, - cancellationToken: cancellationToken)) - .Should() - .ThrowAsync(); - } - - [Theory, AutoNSubstituteData] - internal async Task Should_SetStreamCheckpoint( - [Frozen] IStreamCheckpointWriter writer, - EventStoreClient sut, - string name, - StreamId streamId, - StreamVersion streamVersion, - object state, - CancellationToken cancellationToken) - { - await sut.SetStreamCheckpointAsync( - name, - streamId, - streamVersion, - state, - cancellationToken); - - _ = writer - .Received(1) - .WriteAsync( - name, - streamId, - streamVersion, - state, - cancellationToken); - } - - [Theory, AutoNSubstituteData] - internal Task Should_Throw_On_SetStreamCheckpoint_When_Name_IsNull( - EventStoreClient sut, - StreamId streamId, - StreamVersion streamVersion, - CancellationToken cancellationToken) - => FluentActions - .Awaiting(() => sut.SetStreamCheckpointAsync(null, streamId, streamVersion, null, cancellationToken)) - .Should() - .ThrowAsync(); - - [Theory, AutoNSubstituteData] - internal async Task Should_GetStreamCheckpoint_With_State( - [Frozen] IStreamCheckpointReader reader, - EventStoreClient sut, - string name, - StreamId streamId, - Checkpoint expectedCheckpoint, - CancellationToken cancellationToken) - { - reader - .ReadAsync(default, default, default) - .ReturnsForAnyArgs(expectedCheckpoint); - - var checkpoint = await sut.GetStreamCheckpointAsync( - name, - streamId, - cancellationToken); - - _ = reader - .Received(1) - .ReadAsync( - name, - streamId, - cancellationToken); - - checkpoint - .Should() - .Be(expectedCheckpoint); - } - - [Theory, AutoNSubstituteData] - internal Task Should_Throw_On_GetStreamCheckpoint_With_State_When_Name_IsNull( - EventStoreClient sut, - StreamId streamId, - CancellationToken cancellationToken) - => FluentActions - .Awaiting(() => sut.GetStreamCheckpointAsync(null, streamId, cancellationToken)) - .Should() - .ThrowAsync(); - - [Theory, AutoNSubstituteData] - internal async Task Should_GetStreamCheckpoint_Without_State( - [Frozen] IStreamCheckpointReader reader, - EventStoreClient sut, - string name, - StreamId streamId, - Checkpoint expectedCheckpoint, - CancellationToken cancellationToken) - { - reader - .ReadAsync(default, default, default) - .ReturnsForAnyArgs(expectedCheckpoint); - - var checkpoint = await sut.GetStreamCheckpointAsync( - name, - streamId, - cancellationToken); - - checkpoint - .Should() - .Be(expectedCheckpoint); - } - - [Theory, AutoNSubstituteData] - internal Task Should_Throw_On_GetStreamCheckpoint_Without_State_When_Name_IsNull( - EventStoreClient sut, - StreamId streamId, - CancellationToken cancellationToken) - => FluentActions - .Awaiting(() => sut.GetStreamCheckpointAsync(null, streamId, cancellationToken)) - .Should() - .ThrowAsync(); + streamVersion, + state, + cancellationToken); + } + + [Theory, AutoNSubstituteData] + internal Task Should_Throw_On_SetStreamCheckpoint_When_Name_IsNull( + EventStoreClient sut, + StreamId streamId, + StreamVersion streamVersion, + CancellationToken cancellationToken) + => FluentActions + .Awaiting(() => sut.SetStreamCheckpointAsync(null, streamId, streamVersion, null, cancellationToken)) + .Should() + .ThrowAsync(); + + [Theory, AutoNSubstituteData] + internal async Task Should_GetStreamCheckpoint_With_State( + [Frozen] IStreamCheckpointReader reader, + EventStoreClient sut, + string name, + StreamId streamId, + Checkpoint expectedCheckpoint, + CancellationToken cancellationToken) + { + reader + .ReadAsync(default, default, default) + .ReturnsForAnyArgs(expectedCheckpoint); + + var checkpoint = await sut.GetStreamCheckpointAsync( + name, + streamId, + cancellationToken); + + _ = reader + .Received(1) + .ReadAsync( + name, + streamId, + cancellationToken); + + checkpoint + .Should() + .Be(expectedCheckpoint); } + + [Theory, AutoNSubstituteData] + internal Task Should_Throw_On_GetStreamCheckpoint_With_State_When_Name_IsNull( + EventStoreClient sut, + StreamId streamId, + CancellationToken cancellationToken) + => FluentActions + .Awaiting(() => sut.GetStreamCheckpointAsync(null, streamId, cancellationToken)) + .Should() + .ThrowAsync(); + + [Theory, AutoNSubstituteData] + internal async Task Should_GetStreamCheckpoint_Without_State( + [Frozen] IStreamCheckpointReader reader, + EventStoreClient sut, + string name, + StreamId streamId, + Checkpoint expectedCheckpoint, + CancellationToken cancellationToken) + { + reader + .ReadAsync(default, default, default) + .ReturnsForAnyArgs(expectedCheckpoint); + + var checkpoint = await sut.GetStreamCheckpointAsync( + name, + streamId, + cancellationToken); + + checkpoint + .Should() + .Be(expectedCheckpoint); + } + + [Theory, AutoNSubstituteData] + internal Task Should_Throw_On_GetStreamCheckpoint_Without_State_When_Name_IsNull( + EventStoreClient sut, + StreamId streamId, + CancellationToken cancellationToken) + => FluentActions + .Awaiting(() => sut.GetStreamCheckpointAsync(null, streamId, cancellationToken)) + .Should() + .ThrowAsync(); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs b/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs index b4ac11c..7ab9ce6 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Streams; using Atc.Test; @@ -8,227 +6,226 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Events +namespace Atc.Cosmos.EventStore.Tests.Events; + +public class EventBatchProducerTests { - public class EventBatchProducerTests + private readonly IDateTimeProvider dateTimeProvider; + private readonly IEventNameProvider nameProvider; + private readonly IEventIdProvider eventIdProvider; + private readonly EventBatchProducer sut; + private readonly StreamMetadata metadata; + private readonly StreamWriteOptions options; + private readonly DateTimeOffset expectedTimestamp; + private readonly string expectedName; + private readonly string expectedEventId; + private readonly TestEvent @event; + private readonly EventDocument convertedEvent; + private readonly StreamMetadata convertedMetadata; + + public class TestEvent { - private readonly IDateTimeProvider dateTimeProvider; - private readonly IEventNameProvider nameProvider; - private readonly IEventIdProvider eventIdProvider; - private readonly EventBatchProducer sut; - private readonly StreamMetadata metadata; - private readonly StreamWriteOptions options; - private readonly DateTimeOffset expectedTimestamp; - private readonly string expectedName; - private readonly string expectedEventId; - private readonly TestEvent @event; - private readonly EventDocument convertedEvent; - private readonly StreamMetadata convertedMetadata; - - public class TestEvent - { - public string Id { get; set; } - } + public string Id { get; set; } + } - public EventBatchProducerTests() - { - dateTimeProvider = Substitute.For(); - nameProvider = Substitute.For(); - eventIdProvider = Substitute.For(); - expectedName = "event-name"; - expectedTimestamp = DateTimeOffset.Now; - metadata = new StreamMetadata( - "id", - "pk", - "streamId", - 1, - StreamState.Active, - expectedTimestamp.AddDays(-1)); - options = new StreamWriteOptions - { - CausationId = "A", - CorrelationId = "B", - }; - @event = new Fixture().Create(); - expectedEventId = new Fixture().Create(); - - dateTimeProvider - .GetDateTime() - .Returns(expectedTimestamp); - nameProvider - .GetName(default) - .ReturnsForAnyArgs(expectedName); - eventIdProvider - .CreateUniqueId(default) - .ReturnsForAnyArgs(expectedEventId); - - sut = new EventBatchProducer(dateTimeProvider, eventIdProvider, nameProvider); - var batch = sut.FromEvents( - new[] { @event }, - metadata, - options); - convertedEvent = batch.Documents.First(); - convertedMetadata = batch.Metadata; - } - - [Theory, AutoNSubstituteData] - internal void Can_Convert_One_Event( - EventBatchProducer sut) - { - var batch = sut.FromEvents( - new[] { @event }, - metadata, - options); - - batch - .Documents - .Should() - .HaveCount(1); - } - - [Theory, AutoNSubstituteData] - internal void Can_Convert_Multiple_Events( - TestEvent event1, - TestEvent event2, - TestEvent event3, - EventBatchProducer sut) - { - var batch = sut.FromEvents( - new[] { event1, event2, event3 }, - metadata, - options); - - batch - .Documents - .Should() - .HaveCount(3); - } - - [Fact] - public void Should_Have_NextVersion() - => convertedEvent - .Properties - .Version - .Should() - .Be(metadata.Version.Value + 1); - - [Fact] - public void Id_Should_Be_PropertyEventId() - => convertedEvent - .Id - .Should() - .Be(convertedEvent.Properties.EventId); - - [Fact] - public void Should_Have_EventId() - => convertedEvent - .Properties - .EventId - .Should() - .Be(expectedEventId); - - [Fact] - public void PartitionKey_Should_Be_StreamId() - => convertedEvent - .PartitionKey - .Should() - .Be(convertedEvent.Properties.StreamId.Value); - - [Fact] - public void Should_Set_StreamId() - => convertedEvent - .Properties - .StreamId - .Should() - .Be(metadata.StreamId); - - [Fact] - public void Should_Have_Event_Obejct_Set_As_Data() - => convertedEvent - .Data - .Should() - .Be(@event); - - [Fact] - public void Should_Properties() - => convertedEvent - .Properties - .Should() - .NotBeNull(); - - [Fact] - public void Should_Have_Properties_CausationId_From_Options() - => convertedEvent - .Properties - .CausationId - .Should() - .Be(options.CausationId); - - [Theory, AutoNSubstituteData] - internal void Should_Allow_Null_CausationId( - EventBatchProducer sut) - { - var batch = sut.FromEvents( - new[] { @event }, - metadata, - options: null); - - batch - .Documents - .First() - .Properties - .CausationId - .Should() - .BeNull(); - } - - [Fact] - public void Should_Have_Properties_CorrelationId_From_Options() - => convertedEvent - .Properties - .CorrelationId - .Should() - .Be(options.CorrelationId); - - [Theory, AutoNSubstituteData] - internal void Should_Allow_Null_CorrelationId( - EventBatchProducer sut) + public EventBatchProducerTests() + { + dateTimeProvider = Substitute.For(); + nameProvider = Substitute.For(); + eventIdProvider = Substitute.For(); + expectedName = "event-name"; + expectedTimestamp = DateTimeOffset.Now; + metadata = new StreamMetadata( + "id", + "pk", + "streamId", + 1, + StreamState.Active, + expectedTimestamp.AddDays(-1)); + options = new StreamWriteOptions { - var batch = sut.FromEvents( - new[] { @event }, - metadata, - options: null); - - batch - .Documents - .First() - .Properties - .CorrelationId - .Should() - .BeNull(); - } - - [Fact] - public void Should_Set_Timestamp() - => convertedEvent - .Properties - .Timestamp - .Should() - .Be(expectedTimestamp); - - [Fact] - public void Should_Set_Name() - => convertedEvent - .Properties - .Name - .Should() - .Be(expectedName); - - [Fact] - public void Should_Have_Metadata_State_Active() - => convertedMetadata - .State - .Should() - .Be(StreamState.Active); + CausationId = "A", + CorrelationId = "B", + }; + @event = new Fixture().Create(); + expectedEventId = new Fixture().Create(); + + dateTimeProvider + .GetDateTime() + .Returns(expectedTimestamp); + nameProvider + .GetName(default) + .ReturnsForAnyArgs(expectedName); + eventIdProvider + .CreateUniqueId(default) + .ReturnsForAnyArgs(expectedEventId); + + sut = new EventBatchProducer(dateTimeProvider, eventIdProvider, nameProvider); + var batch = sut.FromEvents( + new[] { @event }, + metadata, + options); + convertedEvent = batch.Documents.First(); + convertedMetadata = batch.Metadata; + } + + [Theory, AutoNSubstituteData] + internal void Can_Convert_One_Event( + EventBatchProducer sut) + { + var batch = sut.FromEvents( + new[] { @event }, + metadata, + options); + + batch + .Documents + .Should() + .HaveCount(1); } + + [Theory, AutoNSubstituteData] + internal void Can_Convert_Multiple_Events( + TestEvent event1, + TestEvent event2, + TestEvent event3, + EventBatchProducer sut) + { + var batch = sut.FromEvents( + new[] { event1, event2, event3 }, + metadata, + options); + + batch + .Documents + .Should() + .HaveCount(3); + } + + [Fact] + public void Should_Have_NextVersion() + => convertedEvent + .Properties + .Version + .Should() + .Be(metadata.Version.Value + 1); + + [Fact] + public void Id_Should_Be_PropertyEventId() + => convertedEvent + .Id + .Should() + .Be(convertedEvent.Properties.EventId); + + [Fact] + public void Should_Have_EventId() + => convertedEvent + .Properties + .EventId + .Should() + .Be(expectedEventId); + + [Fact] + public void PartitionKey_Should_Be_StreamId() + => convertedEvent + .PartitionKey + .Should() + .Be(convertedEvent.Properties.StreamId.Value); + + [Fact] + public void Should_Set_StreamId() + => convertedEvent + .Properties + .StreamId + .Should() + .Be(metadata.StreamId); + + [Fact] + public void Should_Have_Event_Obejct_Set_As_Data() + => convertedEvent + .Data + .Should() + .Be(@event); + + [Fact] + public void Should_Properties() + => convertedEvent + .Properties + .Should() + .NotBeNull(); + + [Fact] + public void Should_Have_Properties_CausationId_From_Options() + => convertedEvent + .Properties + .CausationId + .Should() + .Be(options.CausationId); + + [Theory, AutoNSubstituteData] + internal void Should_Allow_Null_CausationId( + EventBatchProducer sut) + { + var batch = sut.FromEvents( + new[] { @event }, + metadata, + options: null); + + batch + .Documents + .First() + .Properties + .CausationId + .Should() + .BeNull(); + } + + [Fact] + public void Should_Have_Properties_CorrelationId_From_Options() + => convertedEvent + .Properties + .CorrelationId + .Should() + .Be(options.CorrelationId); + + [Theory, AutoNSubstituteData] + internal void Should_Allow_Null_CorrelationId( + EventBatchProducer sut) + { + var batch = sut.FromEvents( + new[] { @event }, + metadata, + options: null); + + batch + .Documents + .First() + .Properties + .CorrelationId + .Should() + .BeNull(); + } + + [Fact] + public void Should_Set_Timestamp() + => convertedEvent + .Properties + .Timestamp + .Should() + .Be(expectedTimestamp); + + [Fact] + public void Should_Set_Name() + => convertedEvent + .Properties + .Name + .Should() + .Be(expectedName); + + [Fact] + public void Should_Have_Metadata_State_Active() + => convertedMetadata + .State + .Should() + .Be(StreamState.Active); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs b/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs index 6208701..db812e1 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Events/EventCatalogTests.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Atc.Cosmos.EventStore.Events; using Atc.Cosmos.EventStore.Tests.Fakes; using Atc.Test; @@ -8,47 +5,46 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Events +namespace Atc.Cosmos.EventStore.Tests.Events; + +public class EventCatalogTests { - public class EventCatalogTests - { - [Theory, AutoNSubstituteData] - internal void Should_Resolve_Type_From_Name( - [Frozen] IReadOnlyDictionary mappings, - EventCatalog sut) - => sut.GetEventType(mappings.Keys.First()) - .Should() - .Be(mappings[mappings.Keys.First()]); + [Theory, AutoNSubstituteData] + internal void Should_Resolve_Type_From_Name( + [Frozen] IReadOnlyDictionary mappings, + EventCatalog sut) + => sut.GetEventType(mappings.Keys.First()) + .Should() + .Be(mappings[mappings.Keys.First()]); - [Theory, AutoNSubstituteData] - internal void ShouldReturn_Null_When_Name_IsNotFound( - EventCatalog sut) - => sut - .GetEventType("non-existing-name") - .Should() - .BeNull(); + [Theory, AutoNSubstituteData] + internal void ShouldReturn_Null_When_Name_IsNotFound( + EventCatalog sut) + => sut + .GetEventType("non-existing-name") + .Should() + .BeNull(); - [Theory, AutoNSubstituteData] - public void Should_Resolve_Name_From_Type( - EventOne evt1, - string evt1Name, - string evt2Name) - => new EventCatalog(new Dictionary - { - { evt1Name, typeof(EventOne) }, - { evt2Name, typeof(EventTwo) }, - }) - .GetName(evt1) - .Should() - .Be(evt1Name); + [Theory, AutoNSubstituteData] + public void Should_Resolve_Name_From_Type( + EventOne evt1, + string evt1Name, + string evt2Name) + => new EventCatalog(new Dictionary + { + { evt1Name, typeof(EventOne) }, + { evt2Name, typeof(EventTwo) }, + }) + .GetName(evt1) + .Should() + .Be(evt1Name); - [Theory, AutoNSubstituteData] - internal void ShouldThrow_When_Objects_Type_IsNotFound( - EventOne evt, - EventCatalog sut) - => FluentActions - .Invoking(() => sut.GetName(evt)) - .Should() - .Throw(); - } + [Theory, AutoNSubstituteData] + internal void ShouldThrow_When_Objects_Type_IsNotFound( + EventOne evt, + EventCatalog sut) + => FluentActions + .Invoking(() => sut.GetName(evt)) + .Should() + .Throw(); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs b/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs index 435fc55..b899ee3 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Fakes/EventOne.cs @@ -1,5 +1,3 @@ -using System; - namespace Atc.Cosmos.EventStore.Tests.Fakes; public record EventOne(string Name, int Number); diff --git a/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs b/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs index 98d7fbd..158eecf 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Fakes/FakeEventDataConverter.cs @@ -1,5 +1,4 @@ -using System; -using System.Text.Json; +using System.Text.Json; using Atc.Cosmos.EventStore.Converters; namespace Atc.Cosmos.EventStore.Tests.Fakes; diff --git a/test/Atc.Cosmos.EventStore.Tests/StreamIdTests.cs b/test/Atc.Cosmos.EventStore.Tests/StreamIdTests.cs index ff56768..48d79ad 100644 --- a/test/Atc.Cosmos.EventStore.Tests/StreamIdTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/StreamIdTests.cs @@ -4,70 +4,69 @@ using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests +namespace Atc.Cosmos.EventStore.Tests; + +public class StreamIdTests { - public class StreamIdTests - { - [Theory, AutoNSubstituteData] - public void Should_Be_Constructed_With_Id( - [Frozen] string id, - StreamId sut) - => sut - .Value - .Should() - .Be(id); + [Theory, AutoNSubstituteData] + public void Should_Be_Constructed_With_Id( + [Frozen] string id, + StreamId sut) + => sut + .Value + .Should() + .Be(id); - [Theory, AutoNSubstituteData] - [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Needed by test")] - [SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Needed by test")] - public void Should_Be_EqualTo( - [Frozen] string id, // The same id will be injected into both left and right. - StreamId left, - StreamId right) - => (left == right) - .Should() - .BeTrue(); + [Theory, AutoNSubstituteData] + [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Needed by test")] + [SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Needed by test")] + public void Should_Be_EqualTo( + [Frozen] string id, // The same id will be injected into both left and right. + StreamId left, + StreamId right) + => (left == right) + .Should() + .BeTrue(); - [Theory, AutoNSubstituteData] - public void Should_Be_EqualTo_Using_String_On_LeftSide( - [Frozen] string id, - StreamId right) - => (id == right) - .Should() - .BeTrue(); + [Theory, AutoNSubstituteData] + public void Should_Be_EqualTo_Using_String_On_LeftSide( + [Frozen] string id, + StreamId right) + => (id == right) + .Should() + .BeTrue(); - [Theory, AutoNSubstituteData] - public void Should_Be_EqualTo_Using_String_On_RightSide( - [Frozen] string id, - StreamId left) - => (left == id) - .Should() - .BeTrue(); + [Theory, AutoNSubstituteData] + public void Should_Be_EqualTo_Using_String_On_RightSide( + [Frozen] string id, + StreamId left) + => (left == id) + .Should() + .BeTrue(); - [Theory, AutoNSubstituteData] - public void Should_Support_Explicit_String_Overload( - [Frozen] string id, - StreamId sut) - => ((string)sut) - .Should() - .Be(id); + [Theory, AutoNSubstituteData] + public void Should_Support_Explicit_String_Overload( + [Frozen] string id, + StreamId sut) + => ((string)sut) + .Should() + .Be(id); - [Theory, AutoNSubstituteData] - public void Should_Support_Getting_StreamId_As_String( - [Frozen] string id, - StreamId sut) - => StreamId - .FromStreamId(sut) - .Should() - .Be(id); + [Theory, AutoNSubstituteData] + public void Should_Support_Getting_StreamId_As_String( + [Frozen] string id, + StreamId sut) + => StreamId + .FromStreamId(sut) + .Should() + .Be(id); - [Theory, AutoNSubstituteData] - public void Should_Support_Getting_StreamId_Using_String( - [Frozen] string id, - StreamId sut) - => StreamId - .ToStreamId(id) - .Should() - .BeEquivalentTo(sut); - } + [Theory, AutoNSubstituteData] + public void Should_Support_Getting_StreamId_Using_String( + [Frozen] string id, + StreamId sut) + => StreamId + .ToStreamId(id) + .Should() + .BeEquivalentTo(sut); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/StreamVersionTests.cs b/test/Atc.Cosmos.EventStore.Tests/StreamVersionTests.cs index baff661..be3efcf 100644 --- a/test/Atc.Cosmos.EventStore.Tests/StreamVersionTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/StreamVersionTests.cs @@ -1,182 +1,180 @@ using System.Diagnostics.CodeAnalysis; -using System.Linq; using Atc.Test; using AutoFixture.Xunit2; using FluentAssertions; using Xunit; -namespace Atc.Cosmos.EventStore.Tests +namespace Atc.Cosmos.EventStore.Tests; + +public class StreamVersionTests { - public class StreamVersionTests - { - [Fact] - public void StartOfStream_Should_Have_Zero_Version_Value() - => StreamVersion - .StartOfStream - .Value - .Should() - .Be(0); - - [Fact] - public void EndOfStream_Should_Have_MaxValue_Version_Value() - => StreamVersion - .EndOfStreamValue - .Should() - .Be(long.MaxValue); - - [Theory, AutoNSubstituteData] - public void Should_Be_Constructed_With_Version( - [Frozen] long version, - StreamVersion sut) - => sut - .Value - .Should() - .Be(version); - - [Theory, AutoNSubstituteData] - [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Needed by test")] - [SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Needed by test")] - public void Should_Be_Equal_When_InnerVersion_Has_The_Same_Value( - [Frozen] long version, - StreamVersion left, - StreamVersion right) - => (left == right) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_Equal_When_Comparing_Long_Value_With_StreamVersion( - [Frozen] long version, - StreamVersion sut) - => (sut == version) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Construct_With_ToStreamVersion( - [Frozen] long version, - StreamVersion sut) - => (sut == StreamVersion.ToStreamVersion(version)) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Can_Get_Version_VLong_Value_Explicit( - [Frozen] long version, - StreamVersion sut) - => ((long)sut) - .Should() - .Be(version); - - [Theory, AutoNSubstituteData] - public void Can_Get_Version_As_Long_FromStreamVersion( - [Frozen] long version, - StreamVersion sut) - => StreamVersion - .FromStreamVersion(sut) - .Should() - .Be(version); - - [Theory, AutoNSubstituteData] - public void Should_NotBe_EqualTo( - [Frozen] long version, - StreamVersion sut) - => (sut != StreamVersion.ToStreamVersion(version + 1)) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_EqualTo( - [Frozen] long version, - StreamVersion sut) - => (sut == StreamVersion.ToStreamVersion(version)) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_NotBe_EqualTo_With_Long( - [Frozen] long version, - StreamVersion sut) - => (sut != version + 1) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_EqualTo_With_Long( - [Frozen] long version, - StreamVersion sut) - => (sut == version) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_GreatherThan( - [Frozen] long version, - StreamVersion sut) - => (sut < version + 1) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_LessThan( - [Frozen] long version, - StreamVersion sut) - => (sut > version - 1) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_GreatherThan_Or_EqualTo( - [Frozen] long version, - StreamVersion sut) - => (sut <= version + 1) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_LessThan_Or_EqualTo( - [Frozen] long version, - StreamVersion sut) - => (sut >= version - 1) - .Should() - .BeTrue(); - - [Theory, AutoNSubstituteData] - public void Should_Be_EqualTo_Using_CompareTo( - [Frozen] long version, - StreamVersion sut) - => sut - .CompareTo(version) - .Should() - .Be(0); - - [Theory, AutoNSubstituteData] - public void Should_Be_GreatherThan_Using_CompareTo( - [Frozen] long version, - StreamVersion sut) - => sut - .CompareTo(version + 1) - .Should() - .Be(-1); - - [Theory, AutoNSubstituteData] - public void Should_Be_LessThan_Using_CompareTo( - [Frozen] long version, - StreamVersion sut) - => sut - .CompareTo(version - 1) - .Should() - .Be(1); - - [Theory, AutoNSubstituteData] - public void Should_Able_To_Sort_By_StreamVersion( - StreamVersion[] sut) - => sut - .OrderBy(s => s) - .Should() - .ContainInOrder( - sut - .OrderBy(s => s.Value) - .ToArray()); - } + [Fact] + public void StartOfStream_Should_Have_Zero_Version_Value() + => StreamVersion + .StartOfStream + .Value + .Should() + .Be(0); + + [Fact] + public void EndOfStream_Should_Have_MaxValue_Version_Value() + => StreamVersion + .EndOfStreamValue + .Should() + .Be(long.MaxValue); + + [Theory, AutoNSubstituteData] + public void Should_Be_Constructed_With_Version( + [Frozen] long version, + StreamVersion sut) + => sut + .Value + .Should() + .Be(version); + + [Theory, AutoNSubstituteData] + [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Needed by test")] + [SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Needed by test")] + public void Should_Be_Equal_When_InnerVersion_Has_The_Same_Value( + [Frozen] long version, + StreamVersion left, + StreamVersion right) + => (left == right) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_Equal_When_Comparing_Long_Value_With_StreamVersion( + [Frozen] long version, + StreamVersion sut) + => (sut == version) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Construct_With_ToStreamVersion( + [Frozen] long version, + StreamVersion sut) + => (sut == StreamVersion.ToStreamVersion(version)) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Can_Get_Version_VLong_Value_Explicit( + [Frozen] long version, + StreamVersion sut) + => ((long)sut) + .Should() + .Be(version); + + [Theory, AutoNSubstituteData] + public void Can_Get_Version_As_Long_FromStreamVersion( + [Frozen] long version, + StreamVersion sut) + => StreamVersion + .FromStreamVersion(sut) + .Should() + .Be(version); + + [Theory, AutoNSubstituteData] + public void Should_NotBe_EqualTo( + [Frozen] long version, + StreamVersion sut) + => (sut != StreamVersion.ToStreamVersion(version + 1)) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_EqualTo( + [Frozen] long version, + StreamVersion sut) + => (sut == StreamVersion.ToStreamVersion(version)) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_NotBe_EqualTo_With_Long( + [Frozen] long version, + StreamVersion sut) + => (sut != version + 1) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_EqualTo_With_Long( + [Frozen] long version, + StreamVersion sut) + => (sut == version) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_GreatherThan( + [Frozen] long version, + StreamVersion sut) + => (sut < version + 1) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_LessThan( + [Frozen] long version, + StreamVersion sut) + => (sut > version - 1) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_GreatherThan_Or_EqualTo( + [Frozen] long version, + StreamVersion sut) + => (sut <= version + 1) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_LessThan_Or_EqualTo( + [Frozen] long version, + StreamVersion sut) + => (sut >= version - 1) + .Should() + .BeTrue(); + + [Theory, AutoNSubstituteData] + public void Should_Be_EqualTo_Using_CompareTo( + [Frozen] long version, + StreamVersion sut) + => sut + .CompareTo(version) + .Should() + .Be(0); + + [Theory, AutoNSubstituteData] + public void Should_Be_GreatherThan_Using_CompareTo( + [Frozen] long version, + StreamVersion sut) + => sut + .CompareTo(version + 1) + .Should() + .Be(-1); + + [Theory, AutoNSubstituteData] + public void Should_Be_LessThan_Using_CompareTo( + [Frozen] long version, + StreamVersion sut) + => sut + .CompareTo(version - 1) + .Should() + .Be(1); + + [Theory, AutoNSubstituteData] + public void Should_Able_To_Sort_By_StreamVersion( + StreamVersion[] sut) + => sut + .OrderBy(s => s) + .Should() + .ContainInOrder( + sut + .OrderBy(s => s.Value) + .ToArray()); } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamInfoReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamInfoReaderTests.cs index f4b0b2e..dd5b655 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamInfoReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamInfoReaderTests.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using AutoFixture.AutoNSubstitute; @@ -8,65 +6,64 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams +namespace Atc.Cosmos.EventStore.Tests.Streams; + +public class StreamInfoReaderTests { - public class StreamInfoReaderTests + [Theory, AutoNSubstituteData] + internal async Task Should_Convert_Into_StreamResponse( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + StreamInfoReader sut, + StreamId streamId, + StreamMetadata expectedMetadata, + CancellationToken cancellationToken) { - [Theory, AutoNSubstituteData] - internal async Task Should_Convert_Into_StreamResponse( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - StreamInfoReader sut, - StreamId streamId, - StreamMetadata expectedMetadata, - CancellationToken cancellationToken) - { - metadataReader - .GetAsync(default, cancellationToken) - .ReturnsForAnyArgs(expectedMetadata); + metadataReader + .GetAsync(default, cancellationToken) + .ReturnsForAnyArgs(expectedMetadata); - var info = await sut - .ReadAsync(streamId, cancellationToken) - .ConfigureAwait(false); + var info = await sut + .ReadAsync(streamId, cancellationToken) + .ConfigureAwait(false); - info - .State - .Should() - .Be(expectedMetadata.State); - info - .StreamId - .Should() - .Be(expectedMetadata.StreamId); - info - .Timestamp - .Should() - .Be(expectedMetadata.Timestamp); - info - .Version - .Should() - .Be(expectedMetadata.Version); - } + info + .State + .Should() + .Be(expectedMetadata.State); + info + .StreamId + .Should() + .Be(expectedMetadata.StreamId); + info + .Timestamp + .Should() + .Be(expectedMetadata.Timestamp); + info + .Version + .Should() + .Be(expectedMetadata.Version); + } - [Theory, AutoNSubstituteData] - internal async Task Should_Read_Metadata_From_Stream( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - StreamInfoReader sut, - StreamId streamId, - StreamMetadata expectedMetadata, - CancellationToken cancellationToken) - { - metadataReader - .GetAsync(default, cancellationToken) - .ReturnsForAnyArgs(expectedMetadata); + [Theory, AutoNSubstituteData] + internal async Task Should_Read_Metadata_From_Stream( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + StreamInfoReader sut, + StreamId streamId, + StreamMetadata expectedMetadata, + CancellationToken cancellationToken) + { + metadataReader + .GetAsync(default, cancellationToken) + .ReturnsForAnyArgs(expectedMetadata); - await sut - .ReadAsync(streamId, cancellationToken) - .ConfigureAwait(false); + await sut + .ReadAsync(streamId, cancellationToken) + .ConfigureAwait(false); - _ = metadataReader - .Received(1) - .GetAsync( - streamId, - cancellationToken); - } + _ = metadataReader + .Received(1) + .GetAsync( + streamId, + cancellationToken); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReadValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReadValidatorTests.cs index 0403ecb..e9555cc 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReadValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReadValidatorTests.cs @@ -1,96 +1,94 @@ -using System; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using FluentAssertions; using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams +namespace Atc.Cosmos.EventStore.Tests.Streams; + +public class StreamReadValidatorTests { - public class StreamReadValidatorTests + [Theory] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 1)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 10)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 100)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] + [InlineAutoNSubstituteData(StreamState.Closed, 5, 10)] + [InlineAutoNSubstituteData(StreamState.Closed, 5, 100)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 1)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 10)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 100)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] + [InlineAutoNSubstituteData(StreamState.New, 5, 10)] + [InlineAutoNSubstituteData(StreamState.New, 5, 100)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 1)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 10)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 100)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] + [InlineAutoNSubstituteData(StreamState.Active, 5, 10)] + [InlineAutoNSubstituteData(StreamState.Active, 5, 100)] + internal void Should_Throw_On_Validate( + StreamState streamState, + long expectedVersion, + long actualVersion, + StreamReadValidator sut, + IStreamMetadata metadata) { - [Theory] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 1)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 10)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 100)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] - [InlineAutoNSubstituteData(StreamState.Closed, 5, 10)] - [InlineAutoNSubstituteData(StreamState.Closed, 5, 100)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 1)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 10)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 100)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] - [InlineAutoNSubstituteData(StreamState.New, 5, 10)] - [InlineAutoNSubstituteData(StreamState.New, 5, 100)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 1)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 10)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 100)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, StreamVersion.StartOfStreamValue)] - [InlineAutoNSubstituteData(StreamState.Active, 5, 10)] - [InlineAutoNSubstituteData(StreamState.Active, 5, 100)] - internal void Should_Throw_On_Validate( - StreamState streamState, - long expectedVersion, - long actualVersion, - StreamReadValidator sut, - IStreamMetadata metadata) - { - metadata - .State - .Returns(streamState); - metadata - .Version - .Returns(actualVersion); + metadata + .State + .Returns(streamState); + metadata + .Version + .Returns(actualVersion); - FluentActions.Invoking( - () => sut.Validate(metadata, expectedVersion)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, expectedVersion)) + .Should() + .Throw(); + } - [Theory] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 0)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 10)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 100)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 0)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 1)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 10)] - [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 100)] - [InlineAutoNSubstituteData(StreamState.Closed, 5, 5)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 0)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 10)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 100)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 0)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 1)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 10)] - [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 100)] - [InlineAutoNSubstituteData(StreamState.New, 5, 5)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 0)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 10)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 100)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 0)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 1)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 10)] - [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 100)] - [InlineAutoNSubstituteData(StreamState.Active, 5, 5)] - internal void Should_Validate( - StreamState streamState, - long expectedVersion, - long actualVersion, - StreamReadValidator sut, - IStreamMetadata metadata) - { - metadata - .State - .Returns(streamState); - metadata - .Version - .Returns(actualVersion); + [Theory] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 0)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 10)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.AnyValue, 100)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.StartOfStreamValue, 0)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 1)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 10)] + [InlineAutoNSubstituteData(StreamState.Closed, StreamVersion.NotEmptyValue, 100)] + [InlineAutoNSubstituteData(StreamState.Closed, 5, 5)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 0)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 10)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.AnyValue, 100)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.StartOfStreamValue, 0)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 1)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 10)] + [InlineAutoNSubstituteData(StreamState.New, StreamVersion.NotEmptyValue, 100)] + [InlineAutoNSubstituteData(StreamState.New, 5, 5)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 0)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 10)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.AnyValue, 100)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.StartOfStreamValue, 0)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 1)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 10)] + [InlineAutoNSubstituteData(StreamState.Active, StreamVersion.NotEmptyValue, 100)] + [InlineAutoNSubstituteData(StreamState.Active, 5, 5)] + internal void Should_Validate( + StreamState streamState, + long expectedVersion, + long actualVersion, + StreamReadValidator sut, + IStreamMetadata metadata) + { + metadata + .State + .Returns(streamState); + metadata + .Version + .Returns(actualVersion); - FluentActions.Invoking( - () => sut.Validate(metadata, expectedVersion)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, expectedVersion)) + .Should() + .NotThrow(); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReaderTests.cs index b2575fc..57cdc4a 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamReaderTests.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using AutoFixture.AutoNSubstitute; @@ -9,171 +6,170 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams +namespace Atc.Cosmos.EventStore.Tests.Streams; + +public class StreamReaderTests { - public class StreamReaderTests + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Read_Metadata_From_StreamId( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + [Frozen, Substitute] IStreamIterator streamIterator, + [Substitute] IAsyncEnumerable enumerable, + StreamReader sut, + StreamId streamId, + CancellationToken cancellationToken) { - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Read_Metadata_From_StreamId( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - [Frozen, Substitute] IStreamIterator streamIterator, - [Substitute] IAsyncEnumerable enumerable, - StreamReader sut, - StreamId streamId, - CancellationToken cancellationToken) - { - streamIterator - .ReadAsync(default, default, default, default) - .ReturnsForAnyArgs(enumerable); - - await ReadStream( - sut, + streamIterator + .ReadAsync(default, default, default, default) + .ReturnsForAnyArgs(enumerable); + + await ReadStream( + sut, + streamId, + StreamVersion.Any, + filter: null, + cancellationToken: cancellationToken); + + await metadataReader + .Received() + .GetAsync( streamId, - StreamVersion.Any, - filter: null, - cancellationToken: cancellationToken); - - await metadataReader - .Received() - .GetAsync( - streamId, - cancellationToken); - } - - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Validate_Metadata_With_From_Version( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - [Frozen, Substitute] IStreamWriteValidator validator, - [Frozen, Substitute] IStreamIterator streamIterator, - [Substitute] IAsyncEnumerable enumerable, - StreamReader sut, - StreamId streamId, - StreamMetadata streamMetadata, - CancellationToken cancellationToken) - { - metadataReader - .GetAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); - - streamIterator - .ReadAsync(default, default, default, default) - .ReturnsForAnyArgs(enumerable); - - await ReadStream( - sut, - streamId, - StreamVersion.Any, - filter: null, - cancellationToken: cancellationToken); - - validator - .Received() - .Validate( - streamMetadata, - StreamVersion.Any); - } - - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Read_From_Iterator( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - [Frozen, Substitute] IStreamIterator streamIterator, - [Substitute] IAsyncEnumerable enumerable, - StreamReader sut, - StreamId streamId, - StreamReadFilter filter, - StreamMetadata streamMetadata, - CancellationToken cancellationToken) - { - metadataReader - .GetAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); + cancellationToken); + } - streamIterator - .ReadAsync(default, default, default, default) - .ReturnsForAnyArgs(enumerable); + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Validate_Metadata_With_From_Version( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + [Frozen, Substitute] IStreamWriteValidator validator, + [Frozen, Substitute] IStreamIterator streamIterator, + [Substitute] IAsyncEnumerable enumerable, + StreamReader sut, + StreamId streamId, + StreamMetadata streamMetadata, + CancellationToken cancellationToken) + { + metadataReader + .GetAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); + + streamIterator + .ReadAsync(default, default, default, default) + .ReturnsForAnyArgs(enumerable); + + await ReadStream( + sut, + streamId, + StreamVersion.Any, + filter: null, + cancellationToken: cancellationToken); + + validator + .Received() + .Validate( + streamMetadata, + StreamVersion.Any); + } - await ReadStream( - sut, + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Read_From_Iterator( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + [Frozen, Substitute] IStreamIterator streamIterator, + [Substitute] IAsyncEnumerable enumerable, + StreamReader sut, + StreamId streamId, + StreamReadFilter filter, + StreamMetadata streamMetadata, + CancellationToken cancellationToken) + { + metadataReader + .GetAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); + + streamIterator + .ReadAsync(default, default, default, default) + .ReturnsForAnyArgs(enumerable); + + await ReadStream( + sut, + streamId, + StreamVersion.Any, + filter: filter, + cancellationToken: cancellationToken); + + streamIterator + .Received(1) + .ReadAsync( streamId, StreamVersion.Any, - filter: filter, - cancellationToken: cancellationToken); - - streamIterator - .Received(1) - .ReadAsync( - streamId, - StreamVersion.Any, - filter, - cancellationToken); - } + filter, + cancellationToken); + } - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Yield_Events_From_StreamIterator( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - [Frozen, Substitute] IStreamIterator streamIterator, - [Substitute] IAsyncEnumerable enumerable, - [Substitute] IAsyncEnumerator enumerator, - StreamReader sut, - StreamId streamId, - StreamMetadata streamMetadata, - IEvent firstEvent, - IEvent secondEvent, - CancellationToken cancellationToken) - { - metadataReader - .GetAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Yield_Events_From_StreamIterator( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + [Frozen, Substitute] IStreamIterator streamIterator, + [Substitute] IAsyncEnumerable enumerable, + [Substitute] IAsyncEnumerator enumerator, + StreamReader sut, + StreamId streamId, + StreamMetadata streamMetadata, + IEvent firstEvent, + IEvent secondEvent, + CancellationToken cancellationToken) + { + metadataReader + .GetAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(streamMetadata)); - streamIterator - .ReadAsync(default, default, default, default) - .ReturnsForAnyArgs(enumerable); + streamIterator + .ReadAsync(default, default, default, default) + .ReturnsForAnyArgs(enumerable); - enumerable - .GetAsyncEnumerator(default) - .ReturnsForAnyArgs(enumerator); + enumerable + .GetAsyncEnumerator(default) + .ReturnsForAnyArgs(enumerator); - enumerator - .Current - .ReturnsForAnyArgs(firstEvent, secondEvent); + enumerator + .Current + .ReturnsForAnyArgs(firstEvent, secondEvent); #pragma warning disable CA2012 // Use ValueTasks correctly - enumerator - .MoveNextAsync() + enumerator + .MoveNextAsync() #pragma warning restore CA2012 // Use ValueTasks correctly - .ReturnsForAnyArgs(new ValueTask(true), new ValueTask(true), new ValueTask(false)); - - // Act - var events = await ReadStream( - sut, - streamId, - StreamVersion.Any, - filter: null, - cancellationToken: cancellationToken); - - // Assert - events - .Should() - .Contain(firstEvent); - events - .Should() - .Contain(secondEvent); - } + .ReturnsForAnyArgs(new ValueTask(true), new ValueTask(true), new ValueTask(false)); + + // Act + var events = await ReadStream( + sut, + streamId, + StreamVersion.Any, + filter: null, + cancellationToken: cancellationToken); + + // Assert + events + .Should() + .Contain(firstEvent); + events + .Should() + .Contain(secondEvent); + } - private static async Task> ReadStream( - StreamReader sut, - StreamId streamId, - StreamVersion streamVersion, - StreamReadFilter filter, - CancellationToken cancellationToken) + private static async Task> ReadStream( + StreamReader sut, + StreamId streamId, + StreamVersion streamVersion, + StreamReadFilter filter, + CancellationToken cancellationToken) + { + var received = new List(); + await foreach (var item in sut.ReadAsync(streamId, streamVersion, filter, cancellationToken).ConfigureAwait(false)) { - var received = new List(); - await foreach (var item in sut.ReadAsync(streamId, streamVersion, filter, cancellationToken).ConfigureAwait(false)) - { - received.Add(item); - } - - return received; + received.Add(item); } + + return received; } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriteValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriteValidatorTests.cs index b26bce0..b61ba1c 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriteValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriteValidatorTests.cs @@ -1,66 +1,64 @@ -using System; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using FluentAssertions; using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams +namespace Atc.Cosmos.EventStore.Tests.Streams; + +public class StreamWriteValidatorTests { - public class StreamWriteValidatorTests + [Theory] + [InlineAutoNSubstituteData(StreamState.Closed, 1, 1)] + [InlineAutoNSubstituteData(StreamState.Closed, 0, 1)] + [InlineAutoNSubstituteData(StreamState.Closed, 10, 1)] + [InlineAutoNSubstituteData(StreamState.New, 0, 1)] + [InlineAutoNSubstituteData(StreamState.New, 0, 10)] + [InlineAutoNSubstituteData(StreamState.Active, 1, 2)] + [InlineAutoNSubstituteData(StreamState.Active, 10, 2)] + internal void Should_Throw_On_Validate( + StreamState streamState, + long streamVersion, + long expectedVersion, + StreamWriteValidator sut, + IStreamMetadata metadata) { - [Theory] - [InlineAutoNSubstituteData(StreamState.Closed, 1, 1)] - [InlineAutoNSubstituteData(StreamState.Closed, 0, 1)] - [InlineAutoNSubstituteData(StreamState.Closed, 10, 1)] - [InlineAutoNSubstituteData(StreamState.New, 0, 1)] - [InlineAutoNSubstituteData(StreamState.New, 0, 10)] - [InlineAutoNSubstituteData(StreamState.Active, 1, 2)] - [InlineAutoNSubstituteData(StreamState.Active, 10, 2)] - internal void Should_Throw_On_Validate( - StreamState streamState, - long streamVersion, - long expectedVersion, - StreamWriteValidator sut, - IStreamMetadata metadata) - { - metadata - .State - .Returns(streamState); - metadata - .Version - .Returns(streamVersion); + metadata + .State + .Returns(streamState); + metadata + .Version + .Returns(streamVersion); - FluentActions.Invoking( - () => sut.Validate(metadata, expectedVersion)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, expectedVersion)) + .Should() + .Throw(); + } - [Theory] - [InlineAutoNSubstituteData(StreamState.New, 0, StreamVersion.AnyValue)] - [InlineAutoNSubstituteData(StreamState.New, 0, StreamVersion.StartOfStreamValue)] - [InlineAutoNSubstituteData(StreamState.Active, 1, 1)] - [InlineAutoNSubstituteData(StreamState.Active, 10, 10)] - [InlineAutoNSubstituteData(StreamState.Active, 10, StreamVersion.AnyValue)] - internal void Should_Validate( - StreamState streamState, - long streamVersion, - long expectedVersion, - StreamWriteValidator sut, - IStreamMetadata metadata) - { - metadata - .State - .Returns(streamState); - metadata - .Version - .Returns(streamVersion); + [Theory] + [InlineAutoNSubstituteData(StreamState.New, 0, StreamVersion.AnyValue)] + [InlineAutoNSubstituteData(StreamState.New, 0, StreamVersion.StartOfStreamValue)] + [InlineAutoNSubstituteData(StreamState.Active, 1, 1)] + [InlineAutoNSubstituteData(StreamState.Active, 10, 10)] + [InlineAutoNSubstituteData(StreamState.Active, 10, StreamVersion.AnyValue)] + internal void Should_Validate( + StreamState streamState, + long streamVersion, + long expectedVersion, + StreamWriteValidator sut, + IStreamMetadata metadata) + { + metadata + .State + .Returns(streamState); + metadata + .Version + .Returns(streamVersion); - FluentActions.Invoking( - () => sut.Validate(metadata, expectedVersion)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, expectedVersion)) + .Should() + .NotThrow(); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriterTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriterTests.cs index f020ed4..c83e3ca 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriterTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/StreamWriterTests.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Atc.Cosmos.EventStore.Streams; using Atc.Test; using AutoFixture.AutoNSubstitute; @@ -9,118 +6,117 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams +namespace Atc.Cosmos.EventStore.Tests.Streams; + +public class StreamWriterTests { - public class StreamWriterTests + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Read_Metadata_From_StreamId( + [Frozen, Substitute] IStreamMetadataReader metadataReader, + [Frozen, Substitute] IStreamBatchWriter eventWriter, + StreamWriter sut, + StreamId streamId, + IReadOnlyList events, + StreamMetadata expected, + CancellationToken cancellationToken) { - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Read_Metadata_From_StreamId( - [Frozen, Substitute] IStreamMetadataReader metadataReader, - [Frozen, Substitute] IStreamBatchWriter eventWriter, - StreamWriter sut, - StreamId streamId, - IReadOnlyList events, - StreamMetadata expected, - CancellationToken cancellationToken) - { - eventWriter - .WriteAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(expected)); + eventWriter + .WriteAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(expected)); + + await sut.WriteAsync( + streamId, + events, + StreamVersion.Any, + options: null, + cancellationToken: cancellationToken); - await sut.WriteAsync( + _ = metadataReader + .Received() + .GetAsync( streamId, - events, - StreamVersion.Any, - options: null, - cancellationToken: cancellationToken); + cancellationToken); + } - _ = metadataReader - .Received() - .GetAsync( - streamId, - cancellationToken); - } + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Validate_Metadata_With_Required_Version( + [Frozen, Substitute] IStreamWriteValidator validator, + [Frozen, Substitute] IStreamBatchWriter eventWriter, + StreamWriter sut, + StreamId streamId, + IReadOnlyList events, + StreamMetadata expected, + CancellationToken cancellationToken) + { + eventWriter + .WriteAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(expected)); - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Validate_Metadata_With_Required_Version( - [Frozen, Substitute] IStreamWriteValidator validator, - [Frozen, Substitute] IStreamBatchWriter eventWriter, - StreamWriter sut, - StreamId streamId, - IReadOnlyList events, - StreamMetadata expected, - CancellationToken cancellationToken) - { - eventWriter - .WriteAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(expected)); + await sut.WriteAsync( + streamId, + events, + StreamVersion.Any, + options: null, + cancellationToken: cancellationToken); - await sut.WriteAsync( - streamId, - events, - StreamVersion.Any, - options: null, - cancellationToken: cancellationToken); + validator + .Received() + .Validate( + expected, + StreamVersion.Any); + } - validator - .Received() - .Validate( - expected, - StreamVersion.Any); - } + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Convert_Events( + [Frozen, Substitute] IEventBatchProducer eventConverter, + [Frozen, Substitute] IStreamBatchWriter eventWriter, + StreamWriter sut, + StreamId streamId, + IReadOnlyList events, + StreamMetadata expected, + CancellationToken cancellationToken) + { + eventWriter + .WriteAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(expected)); - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Convert_Events( - [Frozen, Substitute] IEventBatchProducer eventConverter, - [Frozen, Substitute] IStreamBatchWriter eventWriter, - StreamWriter sut, - StreamId streamId, - IReadOnlyList events, - StreamMetadata expected, - CancellationToken cancellationToken) - { - eventWriter - .WriteAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(expected)); + await sut.WriteAsync( + streamId, + events, + StreamVersion.Any, + options: null, + cancellationToken: cancellationToken); - await sut.WriteAsync( - streamId, + eventConverter + .Received() + .FromEvents( events, - StreamVersion.Any, - options: null, - cancellationToken: cancellationToken); - - eventConverter - .Received() - .FromEvents( - events, - expected, - Arg.Any()); - } + expected, + Arg.Any()); + } - [Theory, AutoNSubstituteData] - internal async ValueTask Should_Return_State_From_EventWriter( - [Frozen, Substitute] IStreamBatchWriter eventWriter, - StreamWriter sut, - StreamId streamId, - IReadOnlyList events, - StreamMetadata expected, - CancellationToken cancellationToken) - { - eventWriter - .WriteAsync(default, default) - .ReturnsForAnyArgs(Task.FromResult(expected)); + [Theory, AutoNSubstituteData] + internal async ValueTask Should_Return_State_From_EventWriter( + [Frozen, Substitute] IStreamBatchWriter eventWriter, + StreamWriter sut, + StreamId streamId, + IReadOnlyList events, + StreamMetadata expected, + CancellationToken cancellationToken) + { + eventWriter + .WriteAsync(default, default) + .ReturnsForAnyArgs(Task.FromResult(expected)); - var result = await sut.WriteAsync( - streamId, - events, - StreamVersion.Any, - options: null, - cancellationToken: cancellationToken); + var result = await sut.WriteAsync( + streamId, + events, + StreamVersion.Any, + options: null, + cancellationToken: cancellationToken); - result - .Should() - .BeEquivalentTo(expected); - } + result + .Should() + .BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamClosedValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamClosedValidatorTests.cs index cfb4048..2d9445c 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamClosedValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamClosedValidatorTests.cs @@ -4,56 +4,55 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams.Validators +namespace Atc.Cosmos.EventStore.Tests.Streams.Validators; + +public class StreamClosedValidatorTests { - public class StreamClosedValidatorTests + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_Stream_IsNew( + IStreamMetadata metadata, + StreamVersion version, + StreamClosedValidator sut) { - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_Stream_IsNew( - IStreamMetadata metadata, - StreamVersion version, - StreamClosedValidator sut) - { - metadata - .State - .Returns(StreamState.New); + metadata + .State + .Returns(StreamState.New); - FluentActions.Invoking( - () => sut.Validate(metadata, version)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, version)) + .Should() + .NotThrow(); + } - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_Stream_IsActive( - IStreamMetadata metadata, - StreamVersion version, - StreamClosedValidator sut) - { - metadata - .State - .Returns(StreamState.Active); + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_Stream_IsActive( + IStreamMetadata metadata, + StreamVersion version, + StreamClosedValidator sut) + { + metadata + .State + .Returns(StreamState.Active); - FluentActions.Invoking( - () => sut.Validate(metadata, version)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, version)) + .Should() + .NotThrow(); + } - [Theory, AutoNSubstituteData] - internal void Should_Throw_When_Stream_IsClosed( - IStreamMetadata metadata, - StreamVersion version, - StreamClosedValidator sut) - { - metadata - .State - .Returns(StreamState.Closed); + [Theory, AutoNSubstituteData] + internal void Should_Throw_When_Stream_IsClosed( + IStreamMetadata metadata, + StreamVersion version, + StreamClosedValidator sut) + { + metadata + .State + .Returns(StreamState.Closed); - FluentActions.Invoking( - () => sut.Validate(metadata, version)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, version)) + .Should() + .Throw(); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamEmptyValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamEmptyValidatorTests.cs index 1627f8e..40fb2b7 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamEmptyValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamEmptyValidatorTests.cs @@ -4,59 +4,58 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams.Validators +namespace Atc.Cosmos.EventStore.Tests.Streams.Validators; + +public class StreamEmptyValidatorTests { - public class StreamEmptyValidatorTests + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_Expected_Version_IsNot_StartOfStream( + IStreamMetadata metadata, + StreamEmptyValidator sut) { - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_Expected_Version_IsNot_StartOfStream( - IStreamMetadata metadata, - StreamEmptyValidator sut) - { - metadata - .State - .Returns(StreamState.Active); + metadata + .State + .Returns(StreamState.Active); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.Any)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.Any)) + .Should() + .NotThrow(); + } - [Theory, AutoNSubstituteData] - internal void Should_Throw_When_Stream_IsRequired_ToBe_StartOfStream_But_Stream_IsNot_StartOfStream( - IStreamMetadata metadata, - StreamEmptyValidator sut) - { - metadata - .State - .Returns(StreamState.Active); - metadata - .Version - .Returns(StreamVersion.FromStreamVersion(1)); + [Theory, AutoNSubstituteData] + internal void Should_Throw_When_Stream_IsRequired_ToBe_StartOfStream_But_Stream_IsNot_StartOfStream( + IStreamMetadata metadata, + StreamEmptyValidator sut) + { + metadata + .State + .Returns(StreamState.Active); + metadata + .Version + .Returns(StreamVersion.FromStreamVersion(1)); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.StartOfStream)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.StartOfStream)) + .Should() + .Throw(); + } - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_Stream_Is_StartOfStream( - IStreamMetadata metadata, - StreamEmptyValidator sut) - { - metadata - .State - .Returns(StreamState.Active); - metadata - .Version - .Returns(StreamVersion.StartOfStream); + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_Stream_Is_StartOfStream( + IStreamMetadata metadata, + StreamEmptyValidator sut) + { + metadata + .State + .Returns(StreamState.Active); + metadata + .Version + .Returns(StreamVersion.StartOfStream); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.StartOfStream)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.StartOfStream)) + .Should() + .NotThrow(); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamExpectedVersionValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamExpectedVersionValidatorTests.cs index b96c9c7..0018716 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamExpectedVersionValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamExpectedVersionValidatorTests.cs @@ -4,59 +4,58 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams.Validators +namespace Atc.Cosmos.EventStore.Tests.Streams.Validators; + +public class StreamExpectedVersionValidatorTests { - public class StreamExpectedVersionValidatorTests + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_ExpectedVersion_IsAny( + IStreamMetadata metadata, + StreamExpectedVersionValidator sut) { - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_ExpectedVersion_IsAny( - IStreamMetadata metadata, - StreamExpectedVersionValidator sut) - { - metadata - .State - .Returns(StreamState.Active); + metadata + .State + .Returns(StreamState.Active); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.Any)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.Any)) + .Should() + .NotThrow(); + } - [Theory, AutoNSubstituteData] - internal void Should_Throw_When_Stream_Exceeds_ExpectedVersion( - IStreamMetadata metadata, - StreamExpectedVersionValidator sut) - { - metadata - .State - .Returns(StreamState.Active); - metadata - .Version - .Returns(StreamVersion.FromStreamVersion(3)); + [Theory, AutoNSubstituteData] + internal void Should_Throw_When_Stream_Exceeds_ExpectedVersion( + IStreamMetadata metadata, + StreamExpectedVersionValidator sut) + { + metadata + .State + .Returns(StreamState.Active); + metadata + .Version + .Returns(StreamVersion.FromStreamVersion(3)); - FluentActions.Invoking( - () => sut.Validate(metadata, 1)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, 1)) + .Should() + .Throw(); + } - [Theory, AutoNSubstituteData] - internal void Should_Validate_When_StreamVersion_Is_ExpectedVersion( - IStreamMetadata metadata, - StreamExpectedVersionValidator sut) - { - metadata - .State - .Returns(StreamState.Active); - metadata - .Version - .Returns(StreamVersion.FromStreamVersion(3)); + [Theory, AutoNSubstituteData] + internal void Should_Validate_When_StreamVersion_Is_ExpectedVersion( + IStreamMetadata metadata, + StreamExpectedVersionValidator sut) + { + metadata + .State + .Returns(StreamState.Active); + metadata + .Version + .Returns(StreamVersion.FromStreamVersion(3)); - FluentActions.Invoking( - () => sut.Validate(metadata, 3)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, 3)) + .Should() + .NotThrow(); } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamNotEmptyValidatorValidatorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamNotEmptyValidatorValidatorTests.cs index dbbb45c..0968283 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamNotEmptyValidatorValidatorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Streams/Validators/StreamNotEmptyValidatorValidatorTests.cs @@ -4,60 +4,59 @@ using NSubstitute; using Xunit; -namespace Atc.Cosmos.EventStore.Tests.Streams.Validators +namespace Atc.Cosmos.EventStore.Tests.Streams.Validators; + +public class StreamNotEmptyValidatorValidatorTests { - public class StreamNotEmptyValidatorValidatorTests + [Theory] + [InlineAutoNSubstituteData(1, StreamState.Active)] + [InlineAutoNSubstituteData(10, StreamState.Active)] + [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.Active)] + [InlineAutoNSubstituteData(1, StreamState.Closed)] + [InlineAutoNSubstituteData(10, StreamState.Closed)] + [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.Closed)] + [InlineAutoNSubstituteData(1, StreamState.New)] + [InlineAutoNSubstituteData(10, StreamState.New)] + [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.New)] + internal void Should_Validate( + long version, + StreamState state, + IStreamMetadata metadata, + StreamNotEmptyValidator sut) { - [Theory] - [InlineAutoNSubstituteData(1, StreamState.Active)] - [InlineAutoNSubstituteData(10, StreamState.Active)] - [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.Active)] - [InlineAutoNSubstituteData(1, StreamState.Closed)] - [InlineAutoNSubstituteData(10, StreamState.Closed)] - [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.Closed)] - [InlineAutoNSubstituteData(1, StreamState.New)] - [InlineAutoNSubstituteData(10, StreamState.New)] - [InlineAutoNSubstituteData(StreamVersion.AnyValue, StreamState.New)] - internal void Should_Validate( - long version, - StreamState state, - IStreamMetadata metadata, - StreamNotEmptyValidator sut) - { - metadata - .State - .Returns(state); - metadata - .Version - .Returns(version); + metadata + .State + .Returns(state); + metadata + .Version + .Returns(version); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.NotEmptyValue)) - .Should() - .NotThrow(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.NotEmptyValue)) + .Should() + .NotThrow(); + } - [Theory] - [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.Active)] - [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.Closed)] - [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.New)] - internal void Should_Throw_When_StreamVersion_Is_Not_StartOfStream( - long version, - StreamState state, - IStreamMetadata metadata, - StreamNotEmptyValidator sut) - { - metadata - .State - .Returns(state); - metadata - .Version - .Returns(version); + [Theory] + [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.Active)] + [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.Closed)] + [InlineAutoNSubstituteData(StreamVersion.StartOfStreamValue, StreamState.New)] + internal void Should_Throw_When_StreamVersion_Is_Not_StartOfStream( + long version, + StreamState state, + IStreamMetadata metadata, + StreamNotEmptyValidator sut) + { + metadata + .State + .Returns(state); + metadata + .Version + .Returns(version); - FluentActions.Invoking( - () => sut.Validate(metadata, StreamVersion.NotEmptyValue)) - .Should() - .Throw(); - } + FluentActions.Invoking( + () => sut.Validate(metadata, StreamVersion.NotEmptyValue)) + .Should() + .Throw(); } } \ No newline at end of file From b2c0d028583cd9ffbd8bbd905bdefe597f12ee7f Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 09:42:35 +0100 Subject: [PATCH 13/23] Upgrade github workflow to use dotnet 7 --- .github/workflows/release-preview.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- .github/workflows/verification.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-preview.yml b/.github/workflows/release-preview.yml index f7d5e75..6835c7b 100644 --- a/.github/workflows/release-preview.yml +++ b/.github/workflows/release-preview.yml @@ -28,10 +28,10 @@ jobs: - name: ⚙️ Setup GIT versioning uses: dotnet/nbgv@v0.4.0 - - name: ⚙️ Setup dotnet 6.0.x + - name: ⚙️ Setup dotnet 7.0.x uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.x' + dotnet-version: '7.0.x' - name: 🛠️ Building library in release mode run: dotnet pack -c Release -o ${GITHUB_WORKSPACE}/packages -p:ContinuousIntegrationBuild=true -p:publicrelease=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed54ce1..cf90c5e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -59,10 +59,10 @@ jobs: with: setAllVars: true - - name: ⚙️ Setup dotnet 6.0.x + - name: ⚙️ Setup dotnet 7.0.x uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.x' + dotnet-version: '7.0.x' - name: 🛠️ Update changelog uses: thomaseizinger/keep-a-changelog-new-release@1.2.1 diff --git a/.github/workflows/verification.yml b/.github/workflows/verification.yml index ad69da6..c1228c3 100644 --- a/.github/workflows/verification.yml +++ b/.github/workflows/verification.yml @@ -33,10 +33,10 @@ jobs: with: setAllVars: true - - name: ⚙️ Setup dotnet 6.0.x + - name: ⚙️ Setup dotnet 7.0.x uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.x' + dotnet-version: '7.0.x' - name: 🛠️ Building libraries in release mode run: dotnet build -c release -p:ContinuousIntegrationBuild=true From 5de014473c2cb0872436acd11f93e6836f321028 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 09:50:03 +0100 Subject: [PATCH 14/23] Upgrade test projects to dotnet 7 --- .../Atc.Cosmos.EventStore.Cqrs.Tests.csproj | 2 +- .../Atc.Cosmos.EventStore.IntegrationTests.csproj | 2 +- .../Atc.Cosmos.EventStore.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj index a97325e..df0564e 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 false diff --git a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj index f00fdcb..4dbcbbb 100644 --- a/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj +++ b/test/Atc.Cosmos.EventStore.IntegrationTests/Atc.Cosmos.EventStore.IntegrationTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 false diff --git a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj index f00fdcb..4dbcbbb 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 false From abf746a2d4528c48c7acfc3a483dd615e628d7a3 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sat, 19 Nov 2022 10:25:27 +0100 Subject: [PATCH 15/23] Improve stream read validation --- src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs | 4 ++-- .../Streams/Validators/StreamNotEmptyValidator.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs index 709d379..bdc8992 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/StateProjector.cs @@ -40,7 +40,7 @@ public async ValueTask ProjectAsync( readValidator.Validate( metadata, - (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any); + command.RequiredVersion?.Value ?? StreamVersion.Any); state.Version = metadata.Version; @@ -50,7 +50,7 @@ public async ValueTask ProjectAsync( await foreach (var evt in eventStore .ReadFromStreamAsync( state.Id, - (StreamVersion?)command.RequiredVersion ?? StreamVersion.Any, + command.RequiredVersion?.Value ?? StreamVersion.Any, cancellationToken: cancellationToken) .ConfigureAwait(false)) { diff --git a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs index 82fb354..3c794f2 100644 --- a/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs +++ b/src/Atc.Cosmos.EventStore/Streams/Validators/StreamNotEmptyValidator.cs @@ -20,7 +20,7 @@ public void Validate(IStreamMetadata metadata, StreamVersion version) metadata.Version, version, StreamConflictReason.StreamIsEmpty, - $"Stream is expected to be empty but found {metadata.Version} events."); + $"Stream is expected to not be empty, but found {metadata.Version.Value} events."); } } } \ No newline at end of file From 5be0982c1488fb6bbe4a5e7b9f1d332f33fa4d9d Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sun, 20 Nov 2022 08:48:26 +0100 Subject: [PATCH 16/23] Introduce IConsumeAnyEvent and IConsumeAnyEventAsync --- .../IConsumeAnyEvent.cs | 13 ++++ .../IConsumeAnyEventAsync.cs | 14 ++++ .../Internal/ConsumeEventMetadata.cs | 34 ++++++-- .../ConsumeAnyEventAsyncMetadataTests.cs | 54 +++++++++++++ .../Internal/ConsumeAnyEventMetadataTests.cs | 54 +++++++++++++ .../ConsumeEventAsyncMetadataTests.cs | 61 +++++++++++++++ .../Internal/ConsumeEventMetadataFixture.cs | 54 +++++++++++++ .../Internal/ConsumeEventMetadataTestSpec.cs | 78 +++++++++++++++++++ .../Internal/ConsumeEventMetadataTests.cs | 61 +++++++++++++++ .../Mocks/MockEvent.cs | 8 ++ .../Mocks/MockEventMetadata.cs | 18 +++++ 11 files changed, 443 insertions(+), 6 deletions(-) create mode 100644 src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEvent.cs create mode 100644 src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEventAsync.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataFixture.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEvent.cs create mode 100644 test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEventMetadata.cs diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEvent.cs b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEvent.cs new file mode 100644 index 0000000..f866e35 --- /dev/null +++ b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEvent.cs @@ -0,0 +1,13 @@ +namespace Atc.Cosmos.EventStore.Cqrs; + +/// +/// Implementing this interface will instruct the framework to +/// call for every +/// event in the event stream. +/// +public interface IConsumeAnyEvent +{ + void Consume( + object evt, + EventMetadata metadata); +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEventAsync.cs b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEventAsync.cs new file mode 100644 index 0000000..db19b90 --- /dev/null +++ b/src/Atc.Cosmos.EventStore.Cqrs/IConsumeAnyEventAsync.cs @@ -0,0 +1,14 @@ +namespace Atc.Cosmos.EventStore.Cqrs; + +/// +/// Implementing this interface will instruct the framework to +/// call for every +/// event in the event stream. +/// +public interface IConsumeAnyEventAsync +{ + Task ConsumeAsync( + object evt, + EventMetadata metadata, + CancellationToken cancellationToken); +} diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs index 23b15b5..a0e43c6 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs @@ -5,6 +5,7 @@ namespace Atc.Cosmos.EventStore.Cqrs.Internal; public abstract class ConsumeEventMetadata { private readonly Dictionary consumeEvents; + private readonly bool canConsumeAnyEvent; protected ConsumeEventMetadata( Type type) @@ -21,14 +22,21 @@ protected ConsumeEventMetadata( .GetRuntimeMethods() .First(m => m.Name.Equals(nameof(ProjectTypedEvent), StringComparison.OrdinalIgnoreCase)) .MakeGenericMethod(t)); + + canConsumeAnyEvent = type + .GetInterfaces() + .Any(t => t.UnderlyingSystemType == typeof(IConsumeAnyEvent) + || t.UnderlyingSystemType == typeof(IConsumeAnyEventAsync)); } public bool CanConsumeEvent(IEvent evt) => consumeEvents - .ContainsKey(evt.Data.GetType()); + .ContainsKey(evt.Data.GetType()) + || canConsumeAnyEvent; public bool IsNotConsumingEvents() - => consumeEvents.Keys.Count == 0; + => consumeEvents.Keys.Count == 0 + && !canConsumeAnyEvent; protected async ValueTask ConsumeAsync( IEvent evt, @@ -48,12 +56,26 @@ protected async ValueTask ConsumeAsync( CorrelationId: evt.Metadata.CorrelationId, CausationId: evt.Metadata.CausationId); - var response = consumeEvents[evt.Data.GetType()] - .Invoke(null, new object[] { projection, evt.Data, metadata, cancellationToken }); + if (consumeEvents.TryGetValue(evt.Data.GetType(), out var method)) + { + var response = method.Invoke(null, new object[] { projection, evt.Data, metadata, cancellationToken }); + + if (response is ValueTask v) + { + await v.ConfigureAwait(false); + } + } - if (response is ValueTask v) + if (canConsumeAnyEvent) { - await v.ConfigureAwait(false); + (projection as IConsumeAnyEvent)?.Consume(evt.Data, metadata); + + if (projection is IConsumeAnyEventAsync consumeAsync) + { + await consumeAsync + .ConsumeAsync(evt.Data, metadata, cancellationToken) + .ConfigureAwait(false); + } } } diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs new file mode 100644 index 0000000..33af0b5 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs @@ -0,0 +1,54 @@ +using FluentAssertions; +using Xunit; +using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; + +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public class ConsumeAnyEventAsyncMetadataTests : + IClassFixture> +{ + private readonly ConsumeEventMetadataFixture fixture; + + public ConsumeAnyEventAsyncMetadataTests( + ConsumeEventMetadataFixture fixture) + => this.fixture = fixture; + + [Fact] + public void IsNotConsumingEvents_Should_False_When_Implementing_IConsumeAnyEventAsync() + => fixture + .IsNotConsumingEvents() + .Should() + .BeFalse(); + + [Fact] + public void Should_Consume_Any_Events() + => fixture + .CanConsumeEvent(fixture.ConsumableEven) + .Should() + .BeTrue(); + + [Fact] + public async Task Should_Project_Consumed_Event() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .EventConsumed + .Should() + .Be(fixture.ConsumableEven.Data); + + [Fact] + public async Task Should_Project_Consumed_Metadata() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .MetadataConsumed + .Should() + .BeEquivalentTo( + new EventMetadata( + fixture.ConsumableEven.Metadata.EventId, + EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), + fixture.ConsumableEven.Metadata.Timestamp, + (long)fixture.ConsumableEven.Metadata.Version, + CorrelationId: fixture.ConsumableEven.Metadata.CorrelationId, + CausationId: fixture.ConsumableEven.Metadata.CausationId)); +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs new file mode 100644 index 0000000..d5dc87b --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs @@ -0,0 +1,54 @@ +using FluentAssertions; +using Xunit; +using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; + +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public class ConsumeAnyEventMetadataTests : + IClassFixture> +{ + private readonly ConsumeEventMetadataFixture fixture; + + public ConsumeAnyEventMetadataTests( + ConsumeEventMetadataFixture fixture) + => this.fixture = fixture; + + [Fact] + public void IsNotConsumingEvents_Should_False_When_Implementing_IConsumeAnyEvent() + => fixture + .IsNotConsumingEvents() + .Should() + .BeFalse(); + + [Fact] + public void Should_Consume_Any_Events() + => fixture + .CanConsumeEvent(fixture.ConsumableEven) + .Should() + .BeTrue(); + + [Fact] + public async Task Should_Project_Consumed_Event() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .EventConsumed + .Should() + .Be(fixture.ConsumableEven.Data); + + [Fact] + public async Task Should_Project_Consumed_Metadata() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .MetadataConsumed + .Should() + .BeEquivalentTo( + new EventMetadata( + fixture.ConsumableEven.Metadata.EventId, + EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), + fixture.ConsumableEven.Metadata.Timestamp, + (long)fixture.ConsumableEven.Metadata.Version, + CorrelationId: fixture.ConsumableEven.Metadata.CorrelationId, + CausationId: fixture.ConsumableEven.Metadata.CausationId)); +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs new file mode 100644 index 0000000..6e06a39 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs @@ -0,0 +1,61 @@ +using FluentAssertions; +using Xunit; +using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; + +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public class ConsumeEventAsyncMetadataTests : + IClassFixture> +{ + private readonly ConsumeEventMetadataFixture fixture; + + public ConsumeEventAsyncMetadataTests( + ConsumeEventMetadataFixture fixture) + => this.fixture = fixture; + + [Fact] + public void IsNotConsumingEvents_Should_False_When_OneOrMore_IConsume_Interfaces_AreImplemented() + => fixture + .IsNotConsumingEvents() + .Should() + .BeFalse(); + + [Fact] + public void CanConsumeEvent_Should_Return_True_When_Event_Is_Consumed() + => fixture + .CanConsumeEvent(fixture.ConsumableEven) + .Should() + .BeTrue(); + + [Fact] + public void CanConsumeEvent_Should_Return_False_When_Event_IsNot_Consumed() + => fixture + .CanConsumeEvent(fixture.NotConsumableEven) + .Should() + .BeFalse(); + + [Fact] + public async Task Should_Project_Consumed_Event() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .EventConsumed + .Should() + .Be(fixture.ConsumableEven.Data); + + [Fact] + public async Task Should_Project_Consumed_Metadata() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .MetadataConsumed + .Should() + .BeEquivalentTo( + new EventMetadata( + fixture.ConsumableEven.Metadata.EventId, + EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), + fixture.ConsumableEven.Metadata.Timestamp, + (long)fixture.ConsumableEven.Metadata.Version, + CorrelationId: fixture.ConsumableEven.Metadata.CorrelationId, + CausationId: fixture.ConsumableEven.Metadata.CausationId)); +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataFixture.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataFixture.cs new file mode 100644 index 0000000..1126458 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataFixture.cs @@ -0,0 +1,54 @@ +using Atc.Cosmos.EventStore.Cqrs.Internal; +using Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; +using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; + +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public class ConsumeEventMetadataFixture : + ConsumeEventMetadata + where TProjection : class, new() +{ + public ConsumeEventMetadataFixture() + : base(typeof(TProjection)) + { + Projection = new(); + ConsumableEven = new MockEvent + { + Data = new ConsumedEvent("evt"), + Metadata = new MockEventMetadata + { + Name = "name", + EventId = "eventId", + StreamId = new StreamId("streamId"), + Timestamp = DateTimeOffset.Now, + Version = 1, + }, + }; + NotConsumableEven = new MockEvent + { + Data = new NotConsumedEvent(), + Metadata = new MockEventMetadata + { + Name = "name", + EventId = "eventId", + StreamId = new StreamId("streamId"), + Timestamp = DateTimeOffset.Now, + Version = 1, + }, + }; + } + + public TProjection Projection { get; } + + public IEvent ConsumableEven { get; } + + public IEvent NotConsumableEven { get; } + + public async Task> ConsumeEventAsync( + IEvent evt) + { + await ConsumeAsync(evt, Projection, CancellationToken.None); + + return this; + } +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs new file mode 100644 index 0000000..f96b610 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs @@ -0,0 +1,78 @@ +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public static class ConsumeEventMetadataTestSpec +{ + public record ConsumedEvent(string Name); + + public record NotConsumedEvent(); + + public class ConsumesOneEvent + : IConsumeEvent + { + public EventMetadata? MetadataConsumed { get; set; } + + public ConsumedEvent? EventConsumed { get; set; } + + public void Consume( + ConsumedEvent evt, + EventMetadata metadata) + { + EventConsumed = evt; + MetadataConsumed = metadata; + } + } + + public class ConsumesOneEventAsync + : IConsumeEventAsync + { + public EventMetadata? MetadataConsumed { get; set; } + + public ConsumedEvent? EventConsumed { get; set; } + + public Task ConsumeAsync( + ConsumedEvent evt, + EventMetadata metadata, + CancellationToken cancellationToken) + { + EventConsumed = evt; + MetadataConsumed = metadata; + + return Task.CompletedTask; + } + } + + public class ConsumesAnyEvent + : IConsumeAnyEvent + { + public EventMetadata? MetadataConsumed { get; set; } + + public object? EventConsumed { get; set; } + + public void Consume( + object evt, + EventMetadata metadata) + { + EventConsumed = evt; + MetadataConsumed = metadata; + } + } + + public class ConsumesAnyEventAsync + : IConsumeAnyEventAsync + { + public EventMetadata? MetadataConsumed { get; set; } + + public object? EventConsumed { get; set; } + + public Task ConsumeAsync( + object evt, + EventMetadata metadata, + CancellationToken cancellationToken) + { + EventConsumed = evt; + MetadataConsumed = metadata; + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs new file mode 100644 index 0000000..10ee43b --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs @@ -0,0 +1,61 @@ +using FluentAssertions; +using Xunit; +using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; + +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Internal; + +public class ConsumeEventMetadataTests : + IClassFixture> +{ + private readonly ConsumeEventMetadataFixture fixture; + + public ConsumeEventMetadataTests( + ConsumeEventMetadataFixture fixture) + => this.fixture = fixture; + + [Fact] + public void IsNotConsumingEvents_Should_False_When_OneOrMore_IConsume_Interfaces_AreImplemented() + => fixture + .IsNotConsumingEvents() + .Should() + .BeFalse(); + + [Fact] + public void CanConsumeEvent_Should_Return_True_When_Event_Is_Consumed() + => fixture + .CanConsumeEvent(fixture.ConsumableEven) + .Should() + .BeTrue(); + + [Fact] + public void CanConsumeEvent_Should_Return_False_When_Event_IsNot_Consumed() + => fixture + .CanConsumeEvent(fixture.NotConsumableEven) + .Should() + .BeFalse(); + + [Fact] + public async Task Should_Project_Consumed_Event() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .EventConsumed + .Should() + .Be(fixture.ConsumableEven.Data); + + [Fact] + public async Task Should_Project_Consumed_Metadata() + => (await fixture + .ConsumeEventAsync(fixture.ConsumableEven)) + .Projection + .MetadataConsumed + .Should() + .BeEquivalentTo( + new EventMetadata( + fixture.ConsumableEven.Metadata.EventId, + EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), + fixture.ConsumableEven.Metadata.Timestamp, + (long)fixture.ConsumableEven.Metadata.Version, + CorrelationId: fixture.ConsumableEven.Metadata.CorrelationId, + CausationId: fixture.ConsumableEven.Metadata.CausationId)); +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEvent.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEvent.cs new file mode 100644 index 0000000..73a38b1 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEvent.cs @@ -0,0 +1,8 @@ +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; + +public class MockEvent : IEvent +{ + public object Data { get; set; } + + public IEventMetadata Metadata { get; set; } +} diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEventMetadata.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEventMetadata.cs new file mode 100644 index 0000000..813c113 --- /dev/null +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/MockEventMetadata.cs @@ -0,0 +1,18 @@ +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; + +public class MockEventMetadata : IEventMetadata +{ + public string EventId { get; set; } + + public string Name { get; set; } + + public string? CorrelationId { get; set; } + + public string? CausationId { get; set; } + + public StreamId StreamId { get; set; } + + public DateTimeOffset Timestamp { get; set; } + + public StreamVersion Version { get; set; } +} From 972066ebee4d91b246100efbcbf30067fea796d2 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Sun, 20 Nov 2022 08:48:50 +0100 Subject: [PATCH 17/23] Update change log with new consume any event interfaces --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a86cd4b..0e25eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Pipeline for controlling event data convertion `IEventDataConverter` - Added custom event data converters to be configured using `EventStoreOptions`. This will enable scenarioes such as converting from one version of an event to another. - Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` but using well known types `FaultedEvent` and `UnknownEvent`. +- Introduced new interfaces `IConsumeAnyEvent` and `IConsumeAnyEventAsync` for consuming any event without specifying it type. ### Removed - Setting `ConfigurationString` when configuring event store options. From bae23eec0929a5699217a8a41c64fa263b22fd02 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Thu, 25 May 2023 16:01:35 +0200 Subject: [PATCH 18/23] User event version as id on cosmos document to enforce insert consistency - EventId is removed from metadata property. --- CHANGELOG.md | 2 ++ .../EventMetadata.cs | 1 - .../Internal/ConsumeEventMetadata.cs | 1 - .../Testing/CommandHandlerTester.cs | 1 - .../Cosmos/GuidEventIdProvider.cs | 9 -------- .../EventStoreOptionsBuilder.cs | 8 ------- .../ServiceCollectionExtensions.cs | 1 - .../Events/EventBatchProducer.cs | 7 +----- .../Events/EventMetadata.cs | 4 ++-- .../Events/IEventIdProvider.cs | 6 ----- src/Atc.Cosmos.EventStore/IEventMetadata.cs | 5 ---- .../ConsumeAnyEventAsyncMetadataTests.cs | 1 - .../Internal/ConsumeAnyEventMetadataTests.cs | 1 - .../ConsumeEventAsyncMetadataTests.cs | 3 +-- .../Internal/ConsumeEventMetadataTests.cs | 1 - .../Mocks/TestProjection.cs | 2 +- .../TestProjectionMissingFilterAttribute.cs | 4 ++-- .../Events/EventBatchProducerTests.cs | 23 ++++--------------- 18 files changed, 13 insertions(+), 67 deletions(-) delete mode 100644 src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs delete mode 100644 src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e25eac..8b1134b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added custom event data converters to be configured using `EventStoreOptions`. This will enable scenarioes such as converting from one version of an event to another. - Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` but using well known types `FaultedEvent` and `UnknownEvent`. - Introduced new interfaces `IConsumeAnyEvent` and `IConsumeAnyEventAsync` for consuming any event without specifying it type. +- Fixed raise condition when 2 command processors tries to add the first event to the same stream concurrently. ### Removed - Setting `ConfigurationString` when configuring event store options. +- `EventId` has been removed from `Metadata`. ## [1.6.8] - 2022-07-06 diff --git a/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs index b28580c..764857c 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/EventMetadata.cs @@ -1,7 +1,6 @@ namespace Atc.Cosmos.EventStore.Cqrs; public record EventMetadata( - string EventId, EventStreamId StreamId, DateTimeOffset Timestamp, long Version, diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs index a0e43c6..2b30fdb 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Internal/ConsumeEventMetadata.cs @@ -49,7 +49,6 @@ protected async ValueTask ConsumeAsync( } var metadata = new EventMetadata( - evt.Metadata.EventId, EventStreamId.FromStreamId(evt.Metadata.StreamId), evt.Metadata.Timestamp, (long)evt.Metadata.Version, diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs index 887f37f..ac06503 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Testing/CommandHandlerTester.cs @@ -110,7 +110,6 @@ await handlerMetadata { CausationId = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), - EventId = Guid.NewGuid().ToString(), Name = "test", Version = version++, Timestamp = DateTimeOffset.UtcNow, diff --git a/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs b/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs deleted file mode 100644 index c3be955..0000000 --- a/src/Atc.Cosmos.EventStore/Cosmos/GuidEventIdProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Atc.Cosmos.EventStore.Events; - -namespace Atc.Cosmos.EventStore.Cosmos; - -internal class GuidEventIdProvider : IEventIdProvider -{ - public string CreateUniqueId(IStreamMetadata metadata) - => Guid.NewGuid().ToString(); -} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs index c92ece8..dee8590 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/EventStoreOptionsBuilder.cs @@ -52,14 +52,6 @@ public EventStoreOptionsBuilder UseCustomDateTimeProvider() return this; } - public EventStoreOptionsBuilder UseCustomEventIdProvider() - where T : class, IEventIdProvider - { - Services.TryAddSingleton(); - - return this; - } - public EventStoreOptionsBuilder UseEvents(Action configure) { var builder = new EventCatalogBuilder(); diff --git a/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs b/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs index 19400aa..e152dca 100644 --- a/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Atc.Cosmos.EventStore/DependencyInjection/ServiceCollectionExtensions.cs @@ -22,7 +22,6 @@ public static IServiceCollection AddEventStore( configure?.Invoke(configureOptions); services.TryAddSingleton(); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs index 16f7aa8..6c7e7c2 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventBatchProducer.cs @@ -5,16 +5,13 @@ namespace Atc.Cosmos.EventStore.Events; internal class EventBatchProducer : IEventBatchProducer { private readonly IDateTimeProvider dateTimeProvider; - private readonly IEventIdProvider eventIdProvider; private readonly IEventNameProvider nameProvider; public EventBatchProducer( IDateTimeProvider dateTimeProvider, - IEventIdProvider eventIdProvider, IEventNameProvider nameProvider) { this.dateTimeProvider = dateTimeProvider; - this.eventIdProvider = eventIdProvider; this.nameProvider = nameProvider; } @@ -58,20 +55,18 @@ private EventDocument Convert( string? causationId, DateTimeOffset timestamp) { - var eventId = eventIdProvider.CreateUniqueId(metadata); var streamId = metadata.StreamId.Value; var name = nameProvider.GetName(evt); return new EventDocument { - Id = eventId, + Id = $"{version}", PartitionKey = streamId, Data = evt, Properties = new EventMetadata { CausationId = causationId, CorrelationId = correlationId, - EventId = eventId, StreamId = streamId, Version = version, Timestamp = timestamp, diff --git a/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs b/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs index e2afdc0..06f0fe5 100644 --- a/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs +++ b/src/Atc.Cosmos.EventStore/Events/EventMetadata.cs @@ -7,8 +7,8 @@ namespace Atc.Cosmos.EventStore.Events; /// internal class EventMetadata : IEventMetadata { - [JsonIgnore] - public string EventId { get; set; } = string.Empty; + ////[JsonIgnore] + ////public string EventId { get; set; } = string.Empty; [JsonPropertyName(EventMetadataNames.EventName)] public string Name { get; set; } = string.Empty; diff --git a/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs b/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs deleted file mode 100644 index 4f5b5a0..0000000 --- a/src/Atc.Cosmos.EventStore/Events/IEventIdProvider.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Atc.Cosmos.EventStore.Events; - -public interface IEventIdProvider -{ - string CreateUniqueId(IStreamMetadata metadata); -} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/IEventMetadata.cs b/src/Atc.Cosmos.EventStore/IEventMetadata.cs index cb7c184..96d9924 100644 --- a/src/Atc.Cosmos.EventStore/IEventMetadata.cs +++ b/src/Atc.Cosmos.EventStore/IEventMetadata.cs @@ -5,11 +5,6 @@ namespace Atc.Cosmos.EventStore; /// public interface IEventMetadata { - /// - /// Gets the unique id of the event. - /// - string EventId { get; } - /// /// Gets the name of the event. /// diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs index 33af0b5..66371f1 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventAsyncMetadataTests.cs @@ -45,7 +45,6 @@ public async Task Should_Project_Consumed_Metadata() .Should() .BeEquivalentTo( new EventMetadata( - fixture.ConsumableEven.Metadata.EventId, EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), fixture.ConsumableEven.Metadata.Timestamp, (long)fixture.ConsumableEven.Metadata.Version, diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs index d5dc87b..d3ec3a3 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeAnyEventMetadataTests.cs @@ -45,7 +45,6 @@ public async Task Should_Project_Consumed_Metadata() .Should() .BeEquivalentTo( new EventMetadata( - fixture.ConsumableEven.Metadata.EventId, EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), fixture.ConsumableEven.Metadata.Timestamp, (long)fixture.ConsumableEven.Metadata.Version, diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs index 6e06a39..84f3238 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventAsyncMetadataTests.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Xunit; using static Atc.Cosmos.EventStore.Cqrs.Tests.Internal.ConsumeEventMetadataTestSpec; @@ -52,7 +52,6 @@ public async Task Should_Project_Consumed_Metadata() .Should() .BeEquivalentTo( new EventMetadata( - fixture.ConsumableEven.Metadata.EventId, EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), fixture.ConsumableEven.Metadata.Timestamp, (long)fixture.ConsumableEven.Metadata.Version, diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs index 10ee43b..9f9ee2d 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTests.cs @@ -52,7 +52,6 @@ public async Task Should_Project_Consumed_Metadata() .Should() .BeEquivalentTo( new EventMetadata( - fixture.ConsumableEven.Metadata.EventId, EventStreamId.FromStreamId(fixture.ConsumableEven.Metadata.StreamId), fixture.ConsumableEven.Metadata.Timestamp, (long)fixture.ConsumableEven.Metadata.Version, diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs index ae0e2cd..e2ec976 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjection.cs @@ -1,7 +1,7 @@ namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; [ProjectionFilter("**")] -internal class TestProjection : IProjection +internal sealed class TestProjection : IProjection { public Task CompleteAsync( CancellationToken cancellationToken) diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs index 918b914..0408227 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Mocks/TestProjectionMissingFilterAttribute.cs @@ -1,6 +1,6 @@ -namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; +namespace Atc.Cosmos.EventStore.Cqrs.Tests.Mocks; -internal class TestProjectionMissingFilterAttribute : IProjection +internal sealed class TestProjectionMissingFilterAttribute : IProjection { public Task CompleteAsync( CancellationToken cancellationToken) diff --git a/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs b/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs index 7ab9ce6..9836630 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Events/EventBatchProducerTests.cs @@ -12,13 +12,11 @@ public class EventBatchProducerTests { private readonly IDateTimeProvider dateTimeProvider; private readonly IEventNameProvider nameProvider; - private readonly IEventIdProvider eventIdProvider; private readonly EventBatchProducer sut; private readonly StreamMetadata metadata; private readonly StreamWriteOptions options; private readonly DateTimeOffset expectedTimestamp; private readonly string expectedName; - private readonly string expectedEventId; private readonly TestEvent @event; private readonly EventDocument convertedEvent; private readonly StreamMetadata convertedMetadata; @@ -32,7 +30,6 @@ public EventBatchProducerTests() { dateTimeProvider = Substitute.For(); nameProvider = Substitute.For(); - eventIdProvider = Substitute.For(); expectedName = "event-name"; expectedTimestamp = DateTimeOffset.Now; metadata = new StreamMetadata( @@ -48,7 +45,6 @@ public EventBatchProducerTests() CorrelationId = "B", }; @event = new Fixture().Create(); - expectedEventId = new Fixture().Create(); dateTimeProvider .GetDateTime() @@ -56,11 +52,8 @@ public EventBatchProducerTests() nameProvider .GetName(default) .ReturnsForAnyArgs(expectedName); - eventIdProvider - .CreateUniqueId(default) - .ReturnsForAnyArgs(expectedEventId); - sut = new EventBatchProducer(dateTimeProvider, eventIdProvider, nameProvider); + sut = new EventBatchProducer(dateTimeProvider, nameProvider); var batch = sut.FromEvents( new[] { @event }, metadata, @@ -111,19 +104,11 @@ public void Should_Have_NextVersion() .Be(metadata.Version.Value + 1); [Fact] - public void Id_Should_Be_PropertyEventId() + public void Id_Should_Be_PropertyVersion() => convertedEvent .Id .Should() - .Be(convertedEvent.Properties.EventId); - - [Fact] - public void Should_Have_EventId() - => convertedEvent - .Properties - .EventId - .Should() - .Be(expectedEventId); + .Be($"{(long)convertedEvent.Properties.Version}"); [Fact] public void PartitionKey_Should_Be_StreamId() @@ -141,7 +126,7 @@ public void Should_Set_StreamId() .Be(metadata.StreamId); [Fact] - public void Should_Have_Event_Obejct_Set_As_Data() + public void Should_Have_Event_Object_Set_As_Data() => convertedEvent .Data .Should() From d96a3c2fbfef87b42c877df05d76aa122b0257d7 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Wed, 31 May 2023 09:24:45 +0200 Subject: [PATCH 19/23] Introduce command handler factory - Rerunning commands now creates a new instance of the handler before applying events. - Command processor is now registered as singleton, eliminating the need for using ICommandProcessorFactory. --- CHANGELOG.md | 8 ++- .../Commands/CommandHandlerFactory.cs | 16 ++++++ .../Commands/CommandProcessor.cs | 53 +++++++++++-------- .../Commands/CommandProcessorFactory.cs | 4 +- .../EventStoreOptionsBuilderExtensions.cs | 3 +- .../ICommandHandlerFactory.cs | 7 +++ 6 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerFactory.cs create mode 100644 src/Atc.Cosmos.EventStore.Cqrs/ICommandHandlerFactory.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1134b..267e1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Pipeline for controlling event data convertion `IEventDataConverter` - Added custom event data converters to be configured using `EventStoreOptions`. This will enable scenarioes such as converting from one version of an event to another. -- Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` but using well known types `FaultedEvent` and `UnknownEvent`. +- Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` by using well known types `FaultedEvent` and `UnknownEvent`. - Introduced new interfaces `IConsumeAnyEvent` and `IConsumeAnyEventAsync` for consuming any event without specifying it type. -- Fixed raise condition when 2 command processors tries to add the first event to the same stream concurrently. +- Command processor is now registered as singleton, eliminating the need for using ICommandProcessorFactory. + +### Fixed +- Raise condition when 2 command processors tries to add the first event to the same stream concurrently. +- Rerunning command now create a new instance of the command processor to clear out any previous state it might contain. ### Removed - Setting `ConfigurationString` when configuring event store options. diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerFactory.cs new file mode 100644 index 0000000..a7f33d8 --- /dev/null +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandHandlerFactory.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Atc.Cosmos.EventStore.Cqrs.Commands; + +internal class CommandHandlerFactory : ICommandHandlerFactory +{ + private readonly IServiceProvider provider; + + public CommandHandlerFactory( + IServiceProvider provider) + => this.provider = provider; + + public ICommandHandler Create() + where TCommand : ICommand + => provider.GetRequiredService>(); +} \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs index 32e05db..a2239e9 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessor.cs @@ -5,26 +5,48 @@ internal class CommandProcessor : ICommandProcessor { private readonly IStateWriter stateWriter; private readonly IStateProjector stateProjector; - private readonly ICommandHandler handler; - private int reruns; + private readonly ICommandHandlerFactory handlerFactory; public CommandProcessor( IStateWriter stateWriter, IStateProjector stateProjector, - ICommandHandler handler) + ICommandHandlerFactory handlerFactory) { this.stateWriter = stateWriter; this.stateProjector = stateProjector; - this.handler = handler; + this.handlerFactory = handlerFactory; } public async ValueTask ExecuteAsync( TCommand command, CancellationToken cancellationToken) + => await SafeExecuteAsync( + command, + GetReruns(command), + cancellationToken) + .ConfigureAwait(false); + + private static ResultType GetResultType(StreamVersionConflictException versionConflict) + => versionConflict.Reason switch + { + StreamConflictReason.StreamIsEmpty => ResultType.NotFound, + StreamConflictReason.StreamIsNotEmpty => ResultType.Exists, + _ => ResultType.Conflict, + }; + + private static int GetReruns(TCommand command) + => command.Behavior == OnConflict.RerunCommand + ? command.BehaviorCount + : 0; + + private async ValueTask SafeExecuteAsync( + TCommand command, + int reruns, + CancellationToken cancellationToken) { try { - reruns = GetReruns(command); + var handler = handlerFactory.Create(); // Read and project events to aggregate (command handler). var state = await stateProjector @@ -60,10 +82,11 @@ await handler } catch (StreamWriteConflictException conflict) { - if (ShouldRerunCommand()) + reruns--; + if (reruns > 0) { return await - ExecuteAsync(command, cancellationToken) + SafeExecuteAsync(command, reruns, cancellationToken) .ConfigureAwait(false); } @@ -80,20 +103,4 @@ await handler GetResultType(versionConflict)); } } - - private static int GetReruns(TCommand command) - => command.Behavior == OnConflict.RerunCommand - ? command.BehaviorCount - : 0; - - private static ResultType GetResultType(StreamVersionConflictException versionConflict) - => versionConflict.Reason switch - { - StreamConflictReason.StreamIsEmpty => ResultType.NotFound, - StreamConflictReason.StreamIsNotEmpty => ResultType.Exists, - _ => ResultType.Conflict, - }; - - private bool ShouldRerunCommand() - => reruns-- > 0; } \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs index f8596b2..110fe02 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/Commands/CommandProcessorFactory.cs @@ -8,9 +8,7 @@ internal class CommandProcessorFactory : ICommandProcessorFactory public CommandProcessorFactory( IServiceProvider provider) - { - this.provider = provider; - } + => this.provider = provider; public ICommandProcessor Create() where TCommand : ICommand diff --git a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs index 6df543a..e641571 100644 --- a/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs +++ b/src/Atc.Cosmos.EventStore.Cqrs/DependencyInjection/EventStoreOptionsBuilderExtensions.cs @@ -20,8 +20,9 @@ public static EventStoreOptionsBuilder UseCQRS( builder.Services.AddSingleton(typeof(IStateProjector<>), typeof(StateProjector<>)); builder.Services.AddSingleton(typeof(IStateWriter<>), typeof(StateWriter<>)); - builder.Services.AddTransient(typeof(ICommandProcessor<>), typeof(CommandProcessor<>)); + builder.Services.AddSingleton(typeof(ICommandProcessor<>), typeof(CommandProcessor<>)); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandlerFactory.cs b/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandlerFactory.cs new file mode 100644 index 0000000..5a4ef03 --- /dev/null +++ b/src/Atc.Cosmos.EventStore.Cqrs/ICommandHandlerFactory.cs @@ -0,0 +1,7 @@ +namespace Atc.Cosmos.EventStore.Cqrs; + +public interface ICommandHandlerFactory +{ + ICommandHandler Create() + where TCommand : ICommand; +} \ No newline at end of file From f4943acfca61469a1521c4c10f79ffe8645167ac Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Wed, 31 May 2023 10:28:34 +0200 Subject: [PATCH 20/23] Update dependencies --- Directory.Build.props | 2 +- src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj | 10 ++++++---- src/Directory.Build.props | 2 +- .../Atc.Cosmos.EventStore.Cqrs.Tests.csproj | 8 ++++---- .../Atc.Cosmos.EventStore.Tests.csproj | 8 ++++---- test/Directory.Build.props | 4 ++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0efd766..5a094fb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -42,7 +42,7 @@ - + \ No newline at end of file diff --git a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj index dd39bd0..a60f81d 100644 --- a/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj +++ b/src/Atc.Cosmos.EventStore/Atc.Cosmos.EventStore.csproj @@ -16,12 +16,14 @@ - - + + - - + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 338c7ce..4a97165 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -55,7 +55,7 @@ - + diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj index df0564e..344165f 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Atc.Cosmos.EventStore.Cqrs.Tests.csproj @@ -6,15 +6,15 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj index 4dbcbbb..0ffa593 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj +++ b/test/Atc.Cosmos.EventStore.Tests/Atc.Cosmos.EventStore.Tests.csproj @@ -6,15 +6,15 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Directory.Build.props b/test/Directory.Build.props index e56f87b..7355aa1 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -11,8 +11,8 @@ - - + + From 90c006ed64f6bbcbe7901d001ab51253a73b69ce Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Wed, 31 May 2023 10:41:03 +0200 Subject: [PATCH 21/23] Optionally configure cosmos client to accept any server certificate --- CHANGELOG.md | 3 +- .../Cosmos/CosmosClientFactory.cs | 32 +++++++---- .../EventStoreClientOptions.cs | 49 +++++------------ .../EventStoreClientOptionsTests.cs | 54 +++++++++---------- 4 files changed, 64 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267e1e7..e8de297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Unknown or invalid events can now be observed through the `IConsumeEvent` and `IConsumeEventAsync` by using well known types `FaultedEvent` and `UnknownEvent`. - Introduced new interfaces `IConsumeAnyEvent` and `IConsumeAnyEventAsync` for consuming any event without specifying it type. - Command processor is now registered as singleton, eliminating the need for using ICommandProcessorFactory. - +- Optionally configure cosmos client to accept any server certificate when using emulator. + ### Fixed - Raise condition when 2 command processors tries to add the first event to the same stream concurrently. - Rerunning command now create a new instance of the command processor to clear out any previous state it might contain. diff --git a/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs b/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs index 267bfb5..12ede1a 100644 --- a/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs +++ b/src/Atc.Cosmos.EventStore/Cosmos/CosmosClientFactory.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Options; @@ -8,26 +9,37 @@ internal sealed class CosmosClientFactory : ICosmosClientFactory, IDisposable private readonly CosmosClient cosmosClient; private bool disposedValue; + [SuppressMessage( + "Critical Vulnerability", + "S4830:Server certificates should be verified during SSL/TLS connections", + Justification = "This is only allowed when running against cosmos emulator")] public CosmosClientFactory( IOptions options, CosmosEventSerializer eventSerializer) { options.Value.CosmosClientOptions.Serializer = eventSerializer; -#pragma warning disable CS0618 // Type or member is obsolete + + if (options.Value.AllowAnyServerCertificate) + { + options.Value.CosmosClientOptions.HttpClientFactory = () + => new HttpClient( + new HttpClientHandler() + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, + CheckCertificateRevocationList = true, + }); + options.Value.CosmosClientOptions.ConnectionMode = ConnectionMode.Gateway; + } + cosmosClient = options.Value.Credential is null - ? options.Value.ConnectionString is not null - ? new CosmosClient( - options.Value.ConnectionString, - options.Value.CosmosClientOptions) - : new CosmosClient( - options.Value.Endpoint, - options.Value.AuthKey, - options.Value.CosmosClientOptions) + ? new CosmosClient( + options.Value.Endpoint, + options.Value.AuthKey, + options.Value.CosmosClientOptions) : new CosmosClient( options.Value.Endpoint, options.Value.Credential, options.Value.CosmosClientOptions); -#pragma warning restore CS0618 // Type or member is obsolete } public CosmosClient GetClient() diff --git a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs index 5706c7d..f37444e 100644 --- a/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs +++ b/src/Atc.Cosmos.EventStore/EventStoreClientOptions.cs @@ -10,30 +10,6 @@ public class EventStoreClientOptions public const string EmulatorEndpoint = "https://localhost:8081/"; public const string EmulatorAuthKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - [Obsolete("Call UseCosmosEmulator instead.")] - public const string CosmosEmulatorConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; - -#pragma warning disable CS0618 // Type or member is obsolete - private string? connectionString = CosmosEmulatorConnectionString; -#pragma warning restore CS0618 // Type or member is obsolete - - [Obsolete("Call UseCredentials instead.")] - public string? ConnectionString - { - get - { - return connectionString; - } - - set - { - connectionString = value; - AuthKey = null; - Endpoint = null; - Credential = null; - } - } - public string EventStoreDatabaseId { get; set; } = "EventStore"; public string EventStoreContainerId { get; set; } = "event-store"; @@ -60,6 +36,11 @@ public string? ConnectionString public TokenCredential? Credential { get; private set; } + public bool AllowAnyServerCertificate { get; private set; } + + public EventStoreClientOptions() + => UseCosmosEmulator(); + /// /// Configure event store to use instead . /// @@ -73,13 +54,10 @@ public void UseCredentials( Credential = credentials ?? throw new ArgumentNullException(nameof(credentials)); Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); AuthKey = null; -#pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; -#pragma warning restore CS0618 // Type or member is obsolete } /// - /// Configure event storte to use AuthKey when connecting to cosmos db. + /// Configure event store to use AuthKey when connecting to cosmos db. /// /// Cosmos account endpoint. /// Authorization key to connect with. @@ -91,21 +69,20 @@ public void UseCredentials( Credential = null; Endpoint = endpoint ?? throw new ArgumentNullException(nameof(endpoint)); AuthKey = authKey ?? throw new ArgumentNullException(nameof(authKey)); -#pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; -#pragma warning restore CS0618 // Type or member is obsolete } /// /// Configure event store to use cosmos emulator. /// - public void UseCosmosEmulator() + /// Optional custom cosmos emulator endpoint. + /// Optionally configure cosmos client to accept any server certificate. + public void UseCosmosEmulator( + string endpoint = EmulatorEndpoint, + bool allowAnyServerCertificate = false) { Credential = null; - Endpoint = EmulatorEndpoint; + Endpoint = endpoint; AuthKey = EmulatorAuthKey; -#pragma warning disable CS0618 // Type or member is obsolete - connectionString = null; -#pragma warning restore CS0618 // Type or member is obsolete + AllowAnyServerCertificate = allowAnyServerCertificate; } } \ No newline at end of file diff --git a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs index 3edb22b..86fc965 100644 --- a/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/EventStoreClientOptionsTests.cs @@ -10,29 +10,11 @@ public class EventStoreClientOptionsTests [Fact] internal void Should_Default_ToCosmosEmulator() { -#pragma warning disable CS0618 // Type or member is obsolete var options = new EventStoreClientOptions(); - options.ConnectionString.Should().Be(EventStoreClientOptions.CosmosEmulatorConnectionString); -#pragma warning restore CS0618 // Type or member is obsolete - options.AuthKey.Should().BeNull(); - options.Endpoint.Should().BeNull(); - options.Credential.Should().BeNull(); - } - - [Fact] - internal void Should_Set_ConnectionString() - { -#pragma warning disable CS0618 // Type or member is obsolete - var options = new EventStoreClientOptions - { - ConnectionString = "connection-string", - }; - - options.ConnectionString.Should().Be("connection-string"); -#pragma warning restore CS0618 // Type or member is obsolete - options.AuthKey.Should().BeNull(); - options.Endpoint.Should().BeNull(); + options.AuthKey.Should().Be(EventStoreClientOptions.EmulatorAuthKey); + options.Endpoint.Should().Be(EventStoreClientOptions.EmulatorEndpoint); + options.AllowAnyServerCertificate.Should().BeFalse(); options.Credential.Should().BeNull(); } @@ -45,9 +27,6 @@ internal void Should_UseAuthKeyAndEndpoint() options.AuthKey.Should().Be("auth-key"); options.Endpoint.Should().Be("endpoint"); options.Credential.Should().BeNull(); -#pragma warning disable CS0618 // Type or member is obsolete - options.ConnectionString.Should().BeNull(); -#pragma warning restore CS0618 // Type or member is obsolete } [Theory, AutoNSubstituteData] @@ -60,8 +39,29 @@ internal void Should_UseCredentialToken( options.Endpoint.Should().Be("endpoint"); options.Credential.Should().Be(token); options.AuthKey.Should().BeNull(); -#pragma warning disable CS0618 // Type or member is obsolete - options.ConnectionString.Should().BeNull(); -#pragma warning restore CS0618 // Type or member is obsolete + } + + [Fact] + internal void Should_AllowAnyServerCertificate_When_UsingEmulator() + { + var options = new EventStoreClientOptions(); + options.UseCosmosEmulator(allowAnyServerCertificate: true); + + options.AuthKey.Should().Be(EventStoreClientOptions.EmulatorAuthKey); + options.Endpoint.Should().Be(EventStoreClientOptions.EmulatorEndpoint); + options.AllowAnyServerCertificate.Should().BeTrue(); + options.Credential.Should().BeNull(); + } + + [Fact] + internal void Should_ConfigureCustomEndpointPort_When_UsingEmulator() + { + var options = new EventStoreClientOptions(); + options.UseCosmosEmulator("https://localhost:10222/"); + + options.AuthKey.Should().Be(EventStoreClientOptions.EmulatorAuthKey); + options.Endpoint.Should().Be("https://localhost:10222/"); + options.AllowAnyServerCertificate.Should().BeFalse(); + options.Credential.Should().BeNull(); } } \ No newline at end of file From fd37ac178686d88785a4d5ca4553892a618654f1 Mon Sep 17 00:00:00 2001 From: LarsSkovslund Date: Wed, 31 May 2023 11:03:15 +0200 Subject: [PATCH 22/23] Fixed build errors --- .../Internal/ConsumeEventMetadataTestSpec.cs | 2 ++ .../Cosmos/CosmosCheckpointReaderTests.cs | 2 ++ .../Cosmos/CosmosMetadataReaderTests.cs | 5 +++++ .../Cosmos/CosmosStreamIteratorTests.cs | 2 ++ 4 files changed, 11 insertions(+) diff --git a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs index f96b610..4e9d080 100644 --- a/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs +++ b/test/Atc.Cosmos.EventStore.Cqrs.Tests/Internal/ConsumeEventMetadataTestSpec.cs @@ -4,7 +4,9 @@ public static class ConsumeEventMetadataTestSpec { public record ConsumedEvent(string Name); +#pragma warning disable S2094 // Classes should not be empty public record NotConsumedEvent(); +#pragma warning restore S2094 // Classes should not be empty public class ConsumesOneEvent : IConsumeEvent diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs index 6f826c2..030ed23 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosCheckpointReaderTests.cs @@ -89,6 +89,7 @@ await sut cancellationToken); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Return_Null_When_Document_IsNotFound( string name, @@ -109,6 +110,7 @@ public async Task Should_Return_Null_When_Document_IsNotFound( .BeNull(); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Propergate_CosmosException_When_StatusCode_IsNot_NotFound( string name, diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs index c0ee20e..1b93ffd 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosMetadataReaderTests.cs @@ -116,6 +116,7 @@ public async Task Should_Return_Response( .BeEquivalentTo(expectedMetadata); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Have_State_New_When_Document_IsNotFound( StreamId streamId, @@ -136,6 +137,7 @@ public async Task Should_Have_State_New_When_Document_IsNotFound( .Be(StreamState.New); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Have_StreamVersion_StartOfStream_When_Document_IsNotFound( StreamId streamId, @@ -156,6 +158,7 @@ public async Task Should_Have_StreamVersion_StartOfStream_When_Document_IsNotFou .Be(StreamVersion.StartOfStream); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Have_Correct_Id_When_Document_IsNotFound( StreamId streamId, @@ -176,6 +179,7 @@ public async Task Should_Have_Correct_Id_When_Document_IsNotFound( .Be(StreamMetadata.StreamMetadataId); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Have_StreamId_As_PartitionKey_When_Document_IsNotFound( StreamId streamId, @@ -196,6 +200,7 @@ public async Task Should_Have_StreamId_As_PartitionKey_When_Document_IsNotFound( .Be(streamId.Value); } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5003:Synchronous exception thrown from async method.", Justification = "Reviewed")] [Theory, AutoNSubstituteData] public async Task Should_Have_Timestamp_When_Document_IsNotFound( StreamId streamId, diff --git a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs index e63fbe8..32139e3 100644 --- a/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs +++ b/test/Atc.Cosmos.EventStore.Tests/Cosmos/CosmosStreamIteratorTests.cs @@ -54,12 +54,14 @@ public CosmosStreamIteratorTests() .Returns(feedResponse); container = Substitute.For(); +#pragma warning disable NS5004 // Argument matcher used with WithAnyArgs. This matcher may not be executed.. container .GetItemQueryIterator( Arg.Do(q => query = q), Arg.Any(), Arg.Do(o => options = o)) .ReturnsForAnyArgs(feedIterator); +#pragma warning restore NS5004 // Argument matcher used with WithAnyArgs. This matcher may not be executed.. containerProvider = Substitute.For(); containerProvider From bec932180230dd2b56659c7b6fc6259d352bf6a8 Mon Sep 17 00:00:00 2001 From: ATCBot Date: Wed, 31 May 2023 09:13:08 +0000 Subject: [PATCH 23/23] Set version to '1.7' --- version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.json b/version.json index aa44cc1..4c3fd6c 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { - "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.7-preview", + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", + "version": "1.7", "assemblyVersion": { "precision": "revision" },